编译期计算阶乘的核心是用template+constexpr+显式特化实现零开销常量展开,类模板递归最稳且兼容性好,变量模板更简洁(c++14起),函数模板需注意c++17 if constexpr支持及odr问题;n必须为编译期常量,否则报错。

编译期计算阶乘不是“能不能”,而是“怎么写才不爆模板递归深度或触发 SFINAE 失败”。核心是用 template + constexpr + 显式特化,让编译器在实例化时展开为常量,运行时零开销。
类模板递归是最稳的写法
这是最经典、兼容性最好、也最容易控制终止条件的方式。它不依赖 C++17 的 if constexpr,连 GCC 4.7 都能跑。
-
Factorial<n></n>主模板负责递推:static constexpr int value = N * Factorial<n-1>::value;</n-1> - 必须提供
Factorial(或Factorial)的全特化作为终止,否则编译失败:错误信息通常是error: incomplete type 'Factorial' used in nested name specifier - 注意:N 必须是编译期常量,比如字面量
5或constexpr int n = 5;;传变量(如int x = 5; Factorial<x>::value</x>)会报错 - 示例:
template<int n> struct Factorial { static constexpr int value = N * Factorial<n>::value; }; <p>template struct Factorial { static constexpr int value = 1; };</p> <p>static_assert(Factorial::value == 720, "compile-time check"); </p></n></int>这段代码在 clang 15 / gcc 12 / MSVC 19.3x 下都稳过。
变量模板更简洁,但要注意 C++14 起支持
如果你用的是 C++14 或更新标准,变量模板写法更轻量,语义也更直白。
- 主定义:
template<int n> inline constexpr int factorial = N * factorial<n-1>;</n-1></int> - 终止特化必须用
template inline constexpr int factorial = 1;,漏掉inline会导致 ODR 违规(尤其多文件编译时) - 和类模板一样,N 不能是运行时值;但比类模板少写
::value,调用更干净:factorial直接是 int 值 - GCC 7+、Clang 3.9+ 支持良好;MSVC 从 15.7 开始稳定支持
函数模板递归容易踩
constexpr if版本陷阱函数模板写法看着像普通函数,但实际行为差异很大——尤其是你混用 C++17
if constexpr和传统特化时。
C++ Code Review Master下载组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用
if constexpr的单函数写法(C++17):template<int n> constexpr int factorial() { if constexpr (N > 0) { return N * factorial<n-1>(); } else { return 1; } } </n-1></int>这种写法简洁,但注意:编译器必须支持 C++17 且开启
-std=c++17;否则if constexpr当成语法错误 - 传统函数模板特化写法(C++11 起)需两段:
template<int n> constexpr int factorial() { return N * factorial<n-1>(); } template constexpr int factorial() { return 1; } </n-1></int>这个版本更老派,但某些嵌入式工具链(如 ARM GCC 6.3)反而更认它
- 常见坑:忘记写
template前缀导致链接时报undefined reference to 'factorial()'
别忽略模板递归深度和数值溢出
编译期计算不是“万能加速”,它把问题从运行时搬到了编译时——而这两处都有硬限制。
- 默认模板递归深度:Clang 默认 256,GCC 默认 900,MSVC 默认 1000。算
Factorial?直接fatal error: template instantiation depth exceeds maximum of ... - 整数溢出发生在编译期:比如
Factorial::value在int上就溢出(14! = 87,178,291,200 > INT_MAX)。别只改类型,要同步改模板参数类型:template<long long n></long>或用constexpr long long - 真正难的不是写出来,而是判断“该不该用”:如果只是偶尔查几个小值(如
n ≤ 10),编译期算很合适;但如果要支持任意输入(比如用户 runtime 输入 n),那必须 fallback 到运行时迭代——编译期方案此时只是个 compile-time assert 辅助工具
最易被忽略的一点:所有这些模板方案都要求 N 是字面量或
constexpr变量。一旦你试图把argv[1]解析后的 int 塞进去,编译就过不了——这不是 bug,是设计使然。得提前想清楚,哪些值真正在编译期可知,哪些必须留到运行时处理。 - 主定义:
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










