计算因子
可以使用模板元编程技术在编译时计算因子。
#include <iostream>
template<unsigned int n>
struct factorial
{
enum
{
value = n * factorial<n - 1>::value
};
};
template<>
struct factorial<0>
{
enum { value = 1 };
};
int main()
{
std::cout << factorial<7>::value << std::endl; // prints "5040"
}
factorial
是一个结构,但在模板元编程中,它被视为模板元函数。按照惯例,模板元函数通过检查特定成员来评估,::type
用于产生类型的元函数,或::value
用于生成值的元函数。
在上面的代码中,我们通过使用我们想要传递的参数实例化模板来评估 factorial
元函数,并使用::value
来获得评估结果。
元函数本身依赖于以较小的值递归地实例化相同的元函数。factorial<0>
专业化代表终止条件。模板元编程具有函数式编程语言的大多数限制,因此递归是主要的循环构造。
由于模板元函数在编译时执行,因此它们的结果可用于需要编译时值的上下文中。例如:
int my_array[factorial<5>::value];
自动数组必须具有编译时定义的大小。元函数的结果是编译时常量,因此可以在这里使用。
限制 :大多数编译器不允许递归深度超出限制。例如,g++
编译器默认情况下将递归限制为 256 级。对于 g++
,程序员可以使用 -ftemplate-depth-X
选项设置递归深度。
Version >= C++ 11
从 C++ 11 开始,std::integral_constant
模板可以用于这种模板计算:
#include <iostream>
#include <type_traits>
template<long long n>
struct factorial :
std::integral_constant<long long, n * factorial<n - 1>::value> {};
template<>
struct factorial<0> :
std::integral_constant<long long, 1> {};
int main()
{
std::cout << factorial<7>::value << std::endl; // prints "5040"
}
此外,constexpr
功能成为更清洁的选择。
#include <iostream>
constexpr long long factorial(long long n)
{
return (n == 0) ? 1 : n * factorial(n - 1);
}
int main()
{
char test[factorial(3)];
std::cout << factorial(7) << '\n';
}
factorial()
的主体被写为单个语句,因为在 C++ 11 中,constexpr
函数只能使用非常有限的语言子集。
Version >= C++ 14
从 C++ 14 开始,对 constexpr
函数的许多限制已被删除,现在可以更方便地编写它们:
constexpr long long factorial(long long n)
{
if (n == 0)
return 1;
else
return n * factorial(n - 1);
}
甚至:
constexpr long long factorial(int n)
{
long long result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
Version >= C++ 17
从 c ++ 17 开始,可以使用 fold 表达式来计算阶乘:
#include <iostream>
#include <utility>
template <class T, T N, class I = std::make_integer_sequence<T, N>>
struct factorial;
template <class T, T N, T... Is>
struct factorial<T,N,std::index_sequence<T, Is...>> {
static constexpr T value = (static_cast<T>(1) * ... * (Is + 1));
};
int main() {
std::cout << factorial<int, 5>::value << std::endl;
}