constexpr函数必须满足:函数体仅含声明、return、字面量运算及constexpr函数调用,禁止static变量、try、goto、asm;c++20前不支持if/循环,局部变量须初始化且类型支持常量表达式。

constexpr 函数必须满足哪些硬性条件
不是所有函数加个 constexpr 就能编译通过。它本质是“编译期可求值”的承诺,编译器会严格检查:函数体只能包含声明、return、字面量运算、调用其他 constexpr 函数(或构造函数),且不能有 static 变量、try、goto、asm 等运行时行为。
常见错误现象:error: constexpr function's body is not a return statement 或 error: call to non-constexpr function。
- 递归必须有明确的终止条件,且深度受限(C++14 起允许有限递归,但编译器可能因展开过深拒绝)
- 局部变量可以声明,但必须初始化,且类型本身需支持常量表达式(比如不能是
std::string) - C++20 前不支持
if和循环语句;C++20 起可用if consteval或constexpr if分支,但分支内仍要满足各自路径的 constexpr 约束
什么时候该用 constexpr 而不是 const 或 #define
const 只保证运行时只读,不一定在编译期可知;#define 是纯文本替换,无类型、无作用域、无法调试。而 constexpr 提供类型安全 + 编译期计算 + 作为模板实参/数组维度/case 标签等场景的准入资格。
典型使用场景:
立即学习“C++免费学习笔记(深入)”;
- 定义模板参数依赖的尺寸,如
std::array<int compute_size_v>></int> - 作为
switch的 case 值(必须是字面量常量表达式) - 初始化
static_assert的条件,例如static_assert(is_power_of_two_v<n>)</n> - 替代宏做编译期字符串处理(C++20
std::string_view+constexpr成员函数)
constexpr 构造函数和用户自定义字面量的实际限制
类要支持 constexpr 构造,必须满足:所有非静态成员都可 constexpr 初始化;构造函数体为空或仅含 constexpr 操作;基类与成员的构造函数也必须是 constexpr。
用户自定义字面量(如 123_km)若声明为 constexpr,其运算符函数必须满足同上约束。但要注意:
- 字符字面量运算符(
operator""_s)接收的是字符数组,C++17 起才支持constexpr版本,且数组长度必须是编译期常量 - 浮点字面量运算符(
operator""_f)在 C++17 前不支持 constexpr,C++20 才完全放开 - 返回类型不能是动态分配对象(如
std::vector),但可以是std::array或自定义 POD 类型
性能提升不是自动发生的:编译器是否真在编译期算出了结果
加了 constexpr 不等于一定在编译期求值——它只是“允许”编译期求值。实际是否发生,取决于上下文是否需要常量表达式。比如 int x = foo(); 中的 foo 是 constexpr,但若 foo() 参数含运行时变量,那这次调用就是运行时执行。
验证方法:
- 用
static_assert(foo(5) == 25)强制编译期求值,失败则说明没达成 - 查看编译后汇编(
g++ -S),确认对应计算是否消失或变为立即数 - 注意 O0 优化级别下,部分 constexpr 可能退化为运行时调用;建议至少用
-O1测试效果
最容易被忽略的一点:模板实例化时传入的 constexpr 值,如果来自未初始化的 constexpr 变量(比如未显式初始化的 constexpr int N;),会导致编译失败——它根本不是常量表达式。










