我正在努力理解模板定义。我的基本理解是模板定义允许将数据类型作为返回或参数作为通用类型。也就是说,模板参数是编译器在编译时实例化和链接的数据类型,
但我无法理解如下的复杂定义:
#include <iostream>
#include <type_traits>
template <unsigned n>
struct factorial : std::integral_constant<int,n * factorial<n-1>::value> {};
template <>
struct factorial<0> : std::integral_constant<int,1> {};
int main() {
std::cout << factorial<5>::value; // constexpr (no calculations on runtime)
return 0;
}
在哪里
template <class T, T v>
struct integral_constant {
static constexpr T value = v;
typedef T value_type;
typedef integral_constant<T,v> type;
constexpr operator T() { return v; }
};
输出:
120
很难想象模板是如何被实例化的:
template <int,5 * factorial<5-1>::value>
struct integral_constant {
static constexpr int value = 5 * factorial<5-1>::value;
typedef int value_type;
typedef integral_constant<5 * factorial<5-1>::value> type;
constexpr operator int() { return 5 * factorial<5-1>::value; }
};
您尚未提及但可能熟悉的模板用途之一是存储通用数据,例如
std::vector<int>
。在这里,我们使用将integral_constant
数据存储为常量静态成员的方式。然后我们
factorial
递归地定义一个结构体,其中基本情况 0 “等于” 1,而任何其他情况都是数字 N 乘以factorial<N - 1>
。这意味着factorial<5>
扩展为:因此,按照继承和乘法,意味着
factorial<5> : std::integral_constant<int, 5 * 4 * 3 * 2 * 1 * 1>
和factorial<5>::value = 5 * 4 * 3 * 2 * 1 * 1 = 120
。尽管语法很奇怪,但这与阶乘作为递归函数的“正常”定义并没有什么不同:
尽管在这两种情况下,我都会建议使用无符号类型或添加负面检查,以避免递归调用爆炸
n < 0
。至于为什么你可能想要以这种方式计算值,这是因为它保证在编译时发生,允许诸如 之类的东西
std::array<int, factorial<5>::value>
。 在现代 C++ 中,这种情况较少见,因为constexpr
函数可以做更多的事情,因此标记int factorial(int n)
为 constexpr 应该允许std::array<int, factorial(5)>
在 C++17 及更新版本中进行编译。