constexpr函数可在编译期计算斐波那契数,比模板元编程更简洁易读;需满足constexpr约束(如仅调用constexpr函数、无副作用、递归深度适中),避免运行时操作或误用模板。

编译期计算斐波那契:用 constexpr 函数最直接
C++11 起,constexpr 函数就能在编译期算斐波那契,比模板元编程简单得多,也更易读。只要函数体满足 constexpr 约束(比如只调用 constexpr 函数、不带副作用、分支有限),递归深度在编译器限制内(通常几百层),就能工作。
常见错误是写成非 constexpr 递归,或者用了 std::cout、new 这类运行时操作;还有人误以为必须用模板——其实不需要。
constexpr int fib(int n) { return n 在 C++14 及以后完全合法- C++11 中需改写为迭代式或尾递归展开(因当时 constexpr 函数体限制极严)
- 调用如
constexpr int x = fib(20);,x的值在编译期确定,生成的汇编里直接是立即数
模板特化实现:适合固定小范围索引
模板方案本质是“为每个 n 实例化一个类型”,靠全特化或偏特化展开递推关系。它不依赖递归调用,而是靠编译器展开模板实例链,因此对旧编译器兼容性更好,但写法冗长、扩展性差。
容易踩的坑是忘记提供基础特化(n=0 和 n=1),导致编译失败;或者用 enum { value = ... } 而不是 static constexpr int value = ...,在 C++17 前无法用于模板非类型参数上下文。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 基础写法:
template<int n> struct fib { static constexpr int value = fib<n-1>::value + fib<n-2>::value; };</n-2></n-1></int> - 必须加两个全特化:
template struct fib { static constexpr int value = 0; };和template struct fib { static constexpr int value = 1; }; - 使用:
constexpr int x = fib::value;—— 注意:fib可能触发模板递归深度超限(GCC 默认 900,Clang 256)
变量模板(C++14):语法更干净的模板方案
比起结构体嵌套 value,变量模板让接口扁平化,一眼看出是值而非类型。它底层仍是模板实例化,但调用形式和 constexpr 函数几乎一样,语义更统一。
注意它不能像函数那样做运行时分支,所有输入必须是字面量常量表达式;另外早期 GCC(如 5.4)对变量模板支持不完整,可能报 error: ‘fib’ declared as a template, but no template parameters were specified。
- 声明:
template<int n> constexpr int fib = fib<n-1> + fib<n-2>;</n-2></n-1></int> - 特化:
template constexpr int fib = 0;,template constexpr int fib = 1; - 使用:
static_assert(fib == 55, "");—— 断言在编译期通过,无运行时开销
为什么不用 std::array 预生成?
有人想用 constexpr std::array 存一整段斐波那契,看似方便,但 C++17 前无法用循环构造(for 不是 constexpr),只能手写或靠外部脚本生成。C++20 引入 std::ranges::iota_view 和更自由的 constexpr 算法后才真正可行,但多数项目仍停留在 C++17。
实际工程中,如果只需要单个值(比如数组长度、位域宽度),用 fib<n></n> 或 fib(N) 就够了;真要整段序列,优先考虑编译期生成头文件,而不是硬塞进模板里。
模板递归深度、constexpr 函数栈帧限制、不同标准版本对 constexpr 的放宽程度——这些才是决定选哪种方式的关键,不是“哪个更酷”。

















