constexpr函数需满足:C++11要求单return语句、参数/返回/内部类型均为字面类型、禁止非constexpr调用、无goto/异常/静态变量;阶乘是典型递归示例。

constexpr 函数必须满足哪些硬性约束
不是所有函数加个 constexpr 就能进编译期求值。C++11 要求函数体必须是「单条 return 语句」(或空函数),且所有参数、返回类型、内部用到的函数/构造都必须是字面类型(literal type)。比如不能有 std::string、std::vector,也不能调用非 constexpr 函数(如 std::sqrt 在 C++11 中不是 constexpr)。
常见错误现象:error: constexpr function's body is not a single return statement 或 error: call to non-constexpr function。
- 只允许一个 return,但可嵌套三元运算符(
a ? b : c)模拟分支 - 局部变量允许声明,但必须是字面类型且初始化为常量表达式(如
constexpr int x = 5;) - 不能有 goto、try/catch、静态局部变量、未初始化变量
- 递归是允许的,但深度受编译器限制(通常几十层),且必须有明确的终止分支
如何写一个真正能在编译期运行的阶乘函数
C++11 下最典型的 constexpr 示例就是阶乘。它验证了递归、字面类型、终止条件等核心规则:
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}这个函数在 factorial(5) 被用作数组长度或模板参数时,会由编译器展开计算;但如果传入变量(如 int x = 5; factorial(x)),则退化为运行时调用(C++11 不强制要求诊断,但多数编译器仍允许,只是不触发编译期求值)。
立即学习“C++免费学习笔记(深入)”;
- 必须用
int等字面类型,不能用long long以外的非 POD 类型 - 参数不能是引用(C++11 不支持
constexpr引用参数),也不支持指针算术 - 若想禁止运行时调用,可配合
static_assert检查是否为常量表达式(需 C++14+ 的is_constant_evaluated()才能精准区分,C++11 只能靠上下文推断)
constexpr 构造函数和字面类怎么配合使用
要让自定义类型参与编译期计算,必须定义 constexpr 构造函数,并确保所有成员都是字面类型、无虚函数、无虚基类。例如一个编译期二维点结构:
struct Point {
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int norm_sq() const { return x_ * x_ + y_ * y_; }
int x_, y_;
};这样就能写 constexpr Point p{3, 4}; constexpr int d2 = p.norm_sq();。但注意:C++11 不允许在 constexpr 成员函数中修改 this(即隐含的 *this 必须是 const),所以所有数据成员应设计为 public 或提供 constexpr getter。
- 成员初始化列表里只能调用 constexpr 构造函数或字面值
- 类中不能有用户定义的析构函数(哪怕空的也不行)
- 继承链上所有基类也必须是字面类型
- 静态成员变量不能在类内初始化为非字面值(如
static constexpr int v = some_runtime_func();是非法的)
为什么有些 constexpr 函数在模板里不生效
典型场景:把 factorial(N) 当作模板非类型参数传入,却报错 non-type template argument is not a constant expression。根本原因往往是 N 本身不是常量表达式——比如来自模板参数但未被声明为 const 或未用字面类型绑定。
- 确保模板参数是字面类型(如
template<int n> struct X</int>,而非template<typename t> struct X</typename>) - 如果 N 来自另一个 constexpr 函数结果,该函数必须满足前述全部约束,且调用时所有实参都是常量表达式
- 某些编译器(如老版本 GCC)对 constexpr 递归深度过于保守,可用
-fconstexpr-depth=128调整(但 C++11 标准只要求至少 512 层) - 别忽略隐式转换:
factorial(5U)(unsigned)可能因类型不匹配导致无法推导为常量表达式,显式转成int更稳妥
最易被忽略的是:C++11 的 constexpr 函数只有在“被用于需要常量表达式的上下文”时才强制编译期求值;否则它只是“可以”被编译期求值,行为与普通函数无异。这意味着调试时看到运行时执行,未必是写错了,而可能是调用方式没触发常量表达式需求。


















