为什么Boost MPL具有积分常数?

时间:2013-01-17 22:37:58

标签: c++ boost c++11 metaprogramming boost-mpl

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

2 个答案:

答案 0 :(得分: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函数之前,没有办法在常量表达式中调用函数,所以要问诸如“这个函数类型需要多少个参数?”之类的问题。您需要返回类型,并从中提取整数值。

即使语言中有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
  }

你可以尝试通过使boolean成为模板参数来做类似的事情:

frob_impl<std::is_copy_constructible<T>::value>(std::forward<T>(t));

但是无法部分专门化功能模板,因此您无法让frob_impl<true, T>frob_impl<false, T>执行不同的操作。在布尔常量的类型上重载允许您根据“is copy constructible”特征的轻松地执行不同的操作,并且 >在C ++ 11中仍然非常有用。

常量有用的另一个地方是使用SFINAE实现特征。在C ++ 03中,传统方法是重载函数返回两种不同大小的类型(例如int和包含两个int s的结构)并用{{1}测试“值” }}。在C ++ 11中,函数可以返回sizeoftrue_type,它更具表现力,例如测试“这种类型是否有一个名为false_type的成员的特征?”可以使函数指示正结果返回foo并使函数指示否定结果返回true_type,还有什么比这更明确?

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

答案 1 :(得分:12)

您可以将整数值作为模板参数,但不能同时使用单个模板同时使用类型非类型模板参数。简而言之,将非类型模板参数视为类型允许它们与 MPL 中的无数事物一起使用。

例如,考虑一个与函数一起使用的元函数find,并在序列中查找相同的类型。如果您希望将其与非类型模板参数一起使用,则需要重新实现新算法的“重载”,find_c,您必须手动指定类型的积分值。现在假设您希望它能够像其他语言一样使用混合积分类型,或者您希望混合类型非类型,你得到了一个“重载”的爆炸式增长,这也很难被使用,因为你必须在任何地方指定每个非类型参数的类型。

这种动机仍然适用于 C ++ 11

此动机仍将适用于 C ++ y 和任何其他版本,除非我们有一些允许从非类型模板参数转换为 type 模板参数。例如,每当您使用5并且模板请求时,类型会使用std::integral_constant< int, 5 >实例化它。