constexpr变量必须在编译期求值,初始化表达式需为常量表达式;constexpr函数可运行时调用但仅当参数为常量表达式时才参与编译期计算;consteval才强制编译期求值。

constexpr 变量必须在编译期能算出值
不是所有带 constexpr 的变量都能通过编译。它要求初始化表达式必须是常量表达式——即不依赖运行时数据、不含未定义行为、只调用 constexpr 函数、访问 constexpr 对象等。比如 constexpr int x = 5 + 3; 合法,但 constexpr int y = rand(); 或 constexpr int z = std::time(nullptr); 直接报错。
常见踩坑点:
- 误以为
constexpr只是“建议编译器优化”,实际是强制编译期求值,失败就编译不过 - 对非字面类型(如自定义类)加
constexpr时,忘了把构造函数、成员函数都标为constexpr - 在 C++14 之前,
constexpr函数体只能含一个 return 表达式;C++17 起支持 if/switch/循环,但所有分支仍需满足常量表达式约束
constexpr 函数要保证“可能”返回编译期结果
constexpr 函数不是每次都被编译期调用,而是“可以被用于常量表达式上下文”。只要传入的参数是常量表达式,且函数执行路径中不触发运行时操作,就能参与编译期计算。
例如:
立即学习“C++免费学习笔记(深入)”;
constexpr int square(int x) {
return x * x;
}
constexpr int a = square(4); // ✅ 编译期算出 16
int b = 5;
int c = square(b); // ✅ 运行时调用,没问题注意:
- 即使函数体里有
if (x > 0),只要调用时x是常量且条件分支可判定,仍可编译期求值 - C++20 引入
consteval才真正强制“必须编译期求值”,和constexpr语义不同 - 调试时别指望在
constexpr函数里打运行时断点——它可能根本没生成机器码
数组长度、模板参数这些地方必须用 constexpr 值
C++ 中很多上下文硬性要求字面量或常量表达式:比如原生数组大小、枚举值、非类型模板参数(NTTP)、static_assert 条件等。这时只有 constexpr 变量或函数调用能填进去。
典型场景:
-
int arr[<code>constexpr_size]; ——constexpr_size必须是constexpr int -
std::array<int>N</int>>; —— 模板参数N必须是常量表达式 -
static_assert(<code>is_valid_v<t></t>, "T must be valid"); —— 第一个参数必须能在编译期确定真假
容易忽略的是:C++17 起允许 constexpr lambda,可用于构造复杂编译期逻辑,但捕获列表必须为空([=] 或 [&] 都不行),否则无法保证纯编译期行为。
constexpr 和 const 的区别不是“能不能改”,而是“什么时候知道值”
const int x = some_runtime_func(); 是运行时常量,值在运行时才确定;constexpr int y = 42; 是编译时常量,连对象地址都可能被折叠进指令。二者语义层级不同。
关键差异:
-
const变量不能被用作数组维度,constexpr可以 -
constexpr对象默认具有内部链接(internal linkage),而const全局变量默认是外部链接,可能引发 ODR 违规 - 类内
static constexpr成员无需类外定义(C++17 起),但static const仍需定义才能取地址
最易被绕过的细节:就算你写了 constexpr,如果用了 new、虚函数调用、dynamic_cast、或任何未标记 constexpr 的函数,整条链就掉出常量表达式范畴——错误往往出现在深层调用栈,得一层层检查函数声明。


















