
c++模板递归实现编译期阶乘
用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 是运行时整数
更现代写法:变量模板(C++14)
避免重复写 ::value,语法更简洁,语义更清晰:
立即学习“C++免费学习笔记(深入)”;
- 需先定义类模板(含特化),再导出变量模板
- 变量模板本身不能偏特化,所以仍依赖类模板特化逻辑
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; };
进阶:函数模板递归(C++14 constexpr 函数)
相比类模板,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 表达式,无法写递归版本。











