既然可以将整数值作为模板参数并对它们进行算术运算,那么
boost::mpl::int_<>
和其他整数常量背后的动机是什么?这种动机在 C++11 中仍然适用吗?
您可以采用整数值作为模板参数,但不能使用单个模板同时采用类型和非类型模板参数。长话短说,将非类型模板参数视为类型允许它们与MPL中的无数事物一起使用。
例如,考虑一个元函数
find
,它处理类型并在序列中查找相等的类型。如果您希望将其与非类型模板参数一起使用,则需要重新实现新算法“重载”,即您必须手动指定整数值的类型。现在想象一下,您希望它像语言的其余部分一样使用混合积分类型,或者您想要混合类型和非类型,您会得到“重载”的爆炸式增长,而这也恰好是更难使用,因为您必须在各处指定每个非类型参数的类型。 这种动机仍然适用于 C++11
。 这种动机仍然适用于
C++y 和任何其他版本,除非我们有一些新规则允许从 非类型模板参数 到 type 模板参数的转换。例如,每当您使用 find_c
并且模板请求类型时,都会用
5
来实例化它。K-Ballo 的回答很棒。
我认为还有其他相关的事情。整型常量类型不仅可用作模板参数,还可用作函数参数和函数返回类型(在我的示例中使用 C++11 类型,但相同的参数适用于早于它们的 Boost 类型):
std::integral_constant< int, 5 >
该函数接受一个函数指针并返回一个类型,告诉您该函数接受的参数数量。在我们拥有
template<typename R, typename... Args>
std::integral_constant<std::size_t, sizeof...(Args)>
arity(R (*)(Args...))
{ return {}; }
函数之前,无法在常量表达式中调用函数,因此要问诸如“此函数类型需要多少个参数?”之类的问题。您需要返回一个
type,并从中提取整数值。 即使语言中有
constexpr
(这意味着上面的函数可以只是
constexpr
并且整数值在编译时可用),整型常量类型仍然有很好的用途,例如标签发送:return sizeof...(Args);
这个
template<typename T>
void frobnicate(T&& t)
{
frob_impl(std::forward<T>(t), std::is_copy_constructible<T>{});
}
函数可以根据作为第二个参数传递的
frob_impl
类型进行重载:integer_constant<bool, b>
您可以尝试通过将布尔值设置为模板参数来执行类似的操作:
template<typename T>
void frob_impl(T&& t, std::true_type)
{
// do something
}
template<typename T>
void frob_impl(T&& t, std::false_type)
{
// do something else
}
但不可能部分专门化函数模板,因此您无法使
frob_impl<std::is_copy_constructible<T>::value>(std::forward<T>(t));
和
frob_impl<true, T>
做不同的事情。重载布尔常量的 type可以让您根据“可复制构造”特征的 value 轻松执行不同的操作,并且 that 在 C++11 中仍然非常有用。 常量有用的另一个地方是使用 SFINAE 实现特征。在 C++03 中,传统方法是重载函数,返回两种不同大小的类型(例如
frob_impl<false, T>
和包含两个
int
的结构),并使用 int
测试“值”。在 C++11 中,函数可以返回 sizeof
和 true_type
,这更具表现力,例如测试“此类型是否有一个名为 false_type
的成员?”的特征可以让表示正结果的函数返回foo
,让表示负结果的函数返回true_type
,还有什么比这更清楚的呢?作为标准库实现者,我非常
频繁使用false_type
和
true_type
,因为很多编译时“问题”都有正确/错误的答案,但是当我想测试可以有更多内容的东西时两个不同的结果我将使用 false_type
的其他专业化。