constexpr函数仅在参数为编译期常量时才执行编译期计算,否则退化为普通函数调用;constexpr变量必须用常量表达式初始化;constexpr构造函数要求所有成员明确初始化;consteval强制编译期求值。

constexpr 函数必须满足“编译期可求值”这个硬约束
不是所有带 constexpr 修饰的函数都能在编译期运行。它要求函数体必须是“简单”的:只能包含声明、return、字面量、其他 constexpr 函数调用,且不能有循环(C++14 起允许 for/while,但循环次数必须在编译期确定)、goto、异常处理等。一旦某次调用传入了运行时值(比如用户输入的 int x),该次调用就退化为普通函数调用——编译器不会报错,但也不会做编译期计算。
- 错误示例:
constexpr int square(int n) { return n * n; }在 C++11 中非法,因为参数n不是字面类型限定;C++14 起允许,但若n是运行时变量,则不触发编译期计算 - 正确写法(C++14+):
constexpr int square(int n) { return n * n; }只有当传入的是字面量或常量表达式(如square(5)或square(N),其中N是constexpr int N = 3;)时,才真正生成编译期常量 - 常见误判点:调试时打印
square(x)结果正常,不代表它被编译期计算了——得看是否用于需要常量表达式的上下文(如数组长度、模板非类型参数)
constexpr 变量必须用常量表达式初始化
constexpr 变量本质是“带类型和名字的编译期常量”,比 const 更严格。它不仅要求不可修改,还要求初始值在编译期就能算出来。哪怕只差一点点(比如调用了一个没加 constexpr 的函数),编译就会失败。
-
const int a = 42;合法,但a不能用作模板参数;constexpr int b = 42;合法,且b可用于std::array - 错误写法:
int runtime_val = 10; constexpr int c = runtime_val; // 编译错误:不是常量表达式
- 注意:
constexpr对象的地址不能取(除非是静态存储期),因为它的生命周期始于编译期;而const全局变量地址可取
constexpr 构造函数让自定义类型也能进编译期
类如果想支持编译期构造(比如作为模板参数、放在 std::array 里),必须提供 constexpr 构造函数,并确保所有成员变量都能在编译期初始化。成员函数也需显式标记为 constexpr 才可能被编译期调用。
- 典型结构:
struct Point { constexpr Point(int x, int y) : x_(x), y_(y) {} constexpr int x() const { return x_; } constexpr int y() const { return y_; } private: int x_, y_; }; - 使用:
constexpr Point p(3, 4);合法;p.x()可用于需要常量表达式的场合 - 容易漏掉的点:即使构造函数是
constexpr,如果成员变量是未初始化的内置类型(如int x_;无默认初始化),整个对象仍无法成为constexpr对象——C++ 要求所有子对象都必须明确初始化
和 const、consteval 混用时的优先级与限制
constexpr 是“可选编译期求值”,consteval(C++20)才是“强制编译期求值”。三者关系不是递进,而是不同语义:const 描述运行时不可变性,constexpr 描述潜在编译期能力,consteval 则彻底禁止运行时调用。
立即学习“C++免费学习笔记(深入)”;
-
consteval int always_compile_time() { return 42; }—— 若写成always_compile_time(x)且x非常量,直接编译失败 - 混用场景:
static constexpr auto value = compute();中,compute()必须是constexpr函数,否则初始化失败;但如果compute()是consteval,则没问题 - 性能提示:过度依赖
constexpr可能拖慢编译速度(尤其复杂计算),而实际运行时未必带来收益——它优化的是“元编程能力”,不是运行时性能本身
编译期常量表达式真正的门槛不在语法,而在你是否清楚每个值的来源是否可追溯到字面量或已知常量;稍有不确定,就退回到运行时,而且往往悄无声息。










