11

既然您可以将整数值作为模板参数并对其进行算术运算,那么 boost::mpl::int_<> 和其他整数常量背后的动机是什么?这种动机是否仍然适用于 C++11?

4

2 回答 2

13

tldr; 将值编码为类型允许它在比简单值更多的地方使用。您可以重载类型,但不能重载值。

K-Ballo 的回答很棒。

我认为还有其他一些相关的东西。整数常量类型不仅可用作模板参数,它们还可用作函数参数和函数返回类型(在我的示例中使用 C++11 类型,但相同的参数适用于早于它们的 Boost 类型):

template<typename R, typename... Args>
  std::integral_constant<std::size_t, sizeof...(Args)> 
  arity(R (*)(Args...))
  { return {}; }

这个函数接受一个函数指针并返回一个类型,告诉你函数接受的参数数量。在我们有constexpr函数之前,没有办法在常量表达式中调用函数,所以要问诸如“这个函数类型需要多少个参数?”之类的问题。您需要返回一个type,并从中提取整数值。

即使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>frob_impl<false, T>做不同的事情。重载布尔常量的类型可以让您轻松地根据“可复制构造”特征的值做不同的事情,在 C++11 中仍然非常有用。

常量有用的另一个地方是使用 SFINAE 实现特征。在 C++03 中,传统方法是使用重载函数返回两种不同大小的类型(例如,一个int和一个包含两个ints 的结构)并使用sizeof. 在 C++11 中,函数可以返回true_type并且false_type更具表现力,例如测试“这种类型是否有一个名为的成员foo?”的特征。可以让表示肯定结果的函数返回,也可以true_type让表示否定结果的函数返回false_type,还有什么比这更清楚的呢?

作为标准库的实现者,我经常使用true_typeand false_type,因为很多编译时“问题”都有真/假答案,但是当我想测试可能有两个以上不同结果的东西时,我会使用其他专业化integral_constant.

于 2013-01-18T00:21:58.533 回答
13

You can take integral values as template parameters, but you cannot take both types and non-type template parameters with a single template. Long story short, treating non-type template parameters as types allows for them to be used with a myriad of things within MPL.

For instance, consider a metafunction find that works with types and looks for an equal type within a sequence. If you wished to use it with non-type template parameters you would need to reimplement new algorithms 'overloads', a find_c for which you have to manually specify the type of the integral value. Now imagine you want it to work with mixed integral types as the rest of the language does, or that you want to mix types and non-types, you get an explosion of 'overloads' that also happen to be harder to use as you have to specify the type of each non-type parameter everywhere.

This motivation does still apply in C++11.

This motivation will still apply to C++y and any other version, unless we have some new rule that allows conversion from non-type template parameters to type template parameters. For instance, whenever you use 5 and the template requests a type instantiate it with std::integral_constant< int, 5 > instead.

于 2013-01-17T22:43:30.810 回答