constexpr函数需满足:函数体仅含声明、return、字面量运算及constexpr函数调用;禁用try、goto、static变量、非字面量类型;参数与返回值须为字面量类型;不可调用非constexpr函数;递归深度受编译器限制。

constexpr 函数必须满足哪些约束?
不是所有函数加个 constexpr 就能进编译期。它本质是“编译器可静态求值的纯函数”,核心限制有:
函数体只能包含声明、return、字面量运算、其他 constexpr 函数调用;不能有 try、goto、static 变量、非字面量类型成员;C++14 起允许有限循环和局部变量,但所有操作仍需在编译期可判定。
- 返回类型和所有参数类型必须是字面量类型(
int、std::array、用户定义的带constexpr构造函数的类等) - 函数体内不能调用非
constexpr函数(比如std::sqrt在 C++20 前不是constexpr) - 递归深度受编译器限制(如 GCC 默认 512 层),过深会报错
constexpr evaluation exceeded maximum depth
如何写一个真正能在编译期展开的阶乘函数?
下面是一个 C++14 兼容的 constexpr 阶乘实现,它在模板实例化或变量初始化时触发编译期计算:
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
// 编译期使用示例
constexpr int fact_5 = factorial(5); // OK:5! = 120,存为字面量
static_assert(fact_5 == 120, "compile-time check");
// 错误用法(运行时输入无法用于 constexpr 上下文)
// int x = 5;
// constexpr int bad = factorial(x); // ❌ error: 'x' is not a constant expression
注意:factorial(5) 在编译时就被替换成 120,生成的汇编里不会出现任何乘法指令;而若传入变量或未标记 constexpr 的表达式,编译器会退化为运行时调用(C++14+)或直接报错(C++11)。
为什么 std::array 的 size() 不是 constexpr?
这是常见误解点:std::array 在 C++17 前**不是** constexpr,导致类似 std::array 无法用于模板非类型参数。原因在于早期标准未将该成员函数标注为 constexpr —— 它只是返回 N,但语言规则没允许。
立即学习“C++免费学习笔记(深入)”;
- C++17 起
std::array::size()已是constexpr,可安全用于模板参数,如:templatevoid f(std::array ); f(arr); - 若需兼容旧标准,应直接用
std::array的模板参数::size() N,而非调用成员函数 - 自定义容器务必显式加
constexpr标签,否则即使逻辑简单也不会被编译器信任
constexpr 和 consteval 的关键区别在哪?
consteval(C++20 引入)是更严格的“仅编译期”保证:它强制函数**必须**在编译期求值,任何运行时调用都会编译失败。而 constexpr 是“可编译期,也可运行时”——取决于调用上下文。
consteval int square(int x) {
return x * x;
}
constexpr int cube(int x) {
return x x x;
}
int main() {
constexpr int s1 = square(4); // ✅ OK
// int s2 = square(4); // ❌ error: call to consteval function 'square' is not a constant expression
constexpr int c1 = cube(4); // ✅ 编译期
int c2 = cube(4); // ✅ 运行时(允许)
}
实际项目中,优先用 constexpr 保持灵活性;只有当你明确禁止运行时路径(例如实现元编程断言、生成唯一编译期哈希),才用 consteval。别为了“看起来更编译期”而滥用 consteval,它会显著降低函数复用性。











