用C++模板递归计算阶乘,本质是利用类模板的特化和实例化过程,在编译阶段完成数值推导,不生成任何运行时代码。核心在于:定义通用模板(递归情况)+ 显式特化(递归终止)。
这是最经典、最易理解的方式。通过 enum 或 constexpr static 成员保存结果:
// C++11 及以上写法(推荐)
templatestruct factorial { static constexpr unsigned long long value = N * factorial ::value; };
// 递归终止:0! = 1 和 1! = 1(可合并为一个特化) template<> struct factorial<0> { static constexpr unsigned long long value = 1; };
// 使用示例 static_assert(factorial<5>::value == 120, "5! should be 120"); int x = factorial<4>::value; // 编译期求得 24,x 是运行时整数
避免重复写 ::value,语法更简洁,语义更清晰:
templatestruct factorial_impl { static constexpr unsigned long long value = N * factorial_impl ::value; }; template<> struct factorial_impl<0> { static constexpr unsigned long long value = 1; };
// 变量模板:直接访问 template
constexpr unsigned long long factorial = factorial_impl ::value; // 使用 static_assert(factorial<6> == 720, "");
模板递归不是万能的,需主动防范溢出和非法输入:
unsigned int)天然拒绝负值,但若用 int,N-1 在 N==0 时会回绕成大正数,导致无限实例化失败(编译错误)factorial 在 unsigned long long 下就溢出(实际值超 2^64),但编译器通常不报错,只得到错误结果;建议配合 static_assert 检查上限N(如 >1000)可能触发编译器模板递归深度限制(如 GCC 默认 900),可用 -ftemplate-depth= 调整,但应优先优化逻辑// 加入上限检查示例(C++17)
templatestruct factorial { static_assert(N < 21, "factorial: N too large, risk of overflow"); static constexpr unsigned long long value = N * factorial ::value; }; template<> struct factorial<0> { static constexpr unsigned long long value = 1; };
相比类模板,constexpr 函数写法更直观,且天然支持运行时调用(一鱼两吃):
constexpr unsigned long long factorial(unsigned int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
// 编译期使用
constexpr auto f5 = factorial(5); // OK,值为 120
static_assert(f5 == 120, "");
// 运行时也可用
int y = factorial(7); // 生成运行时调用(或内联)
注意:该函数在 C++14 中已允许循环和递归;C++11 仅支持单条 return 表达式,无法写递归版本。