constexpr函数必须在需要常量表达式时编译期求值,const仅表示运行时不可修改;前者用于模板参数、数组长度等,后者初始化可为任意表达式,二者语义与约束根本不同。

constexpr 函数必须能在编译期求值,但 const 不要求
关键区别不在“能不能”,而在“要不要”。constexpr 函数被用在需要常量表达式的地方(比如数组长度、模板非类型参数),编译器**必须**尝试在编译期执行它;而 const 变量只是声明“不可修改”,它的初始化表达式可以是运行时值,编译器完全不关心是否能算出来。
常见错误现象:constexpr int x = rand(); 直接报错 —— rand() 不是常量表达式,constexpr 不允许;但 const int y = rand(); 合法(只要在定义时调用)。
-
constexpr函数体受严格限制:不能有 goto、静态局部变量、try/catch、new/delete,C++14 起才允许局部变量和循环 -
const变量的初始化器可以是任意可执行表达式,哪怕调用复杂函数或读文件(当然得在运行时) - 一个函数加了
constexpr修饰,不代表每次调用都在编译期发生——只有当上下文明确需要常量表达式时(如int arr[func(3)];),才强制编译期求值
模板推导和非类型模板参数只认 constexpr,不认 const
这是元编程里最常踩的坑。模板参数必须是编译期已知的字面值,const 变量哪怕值是字面量,也不自动获得“模板可用”资格。
使用场景:写类型萃取、尺寸计算、编译期字符串处理等。
立即学习“C++免费学习笔记(深入)”;
示例:
template<int N>
struct FixedArray { /* ... */ };
<p>constexpr int get_size() { return 16; }
const int size_v = 16;</p><p>FixedArray<get_size()> a; // ✅ OK
FixedArray<size_v> b; // ❌ 错误:‘size_v’ 不是常量表达式</p>-
const变量要变成模板参数,必须显式加constexpr(或用字面量直接写,如FixedArray) - C++20 起,
const变量若初始化为常量表达式,且声明为constexpr或位于consteval上下文中,才可能被接受;单纯const仍不行 - 注意:
const成员变量也不能用于非类型模板参数,除非该类本身是字面类型且成员被constexpr初始化
constexpr 构造函数 ≠ 对象一定是编译期创建
一个类有 constexpr 构造函数,只说明它**有可能**在编译期构造对象;是否真发生,取决于对象的声明方式和使用上下文。
容易踩的坑:以为写了 constexpr MyClass x{1,2}; 就进了编译期,结果发现 x 的地址在运行时才确定,或者被优化掉后根本没生成代码。
- 对象必须声明为
constexpr(不只是const),才能保证编译期构造:constexpr MyClass x{1,2}; - 即使构造函数是
constexpr,如果用了运行时值(如函数参数、用户输入),那这次构造就只能在运行时进行 - 编译期构造的对象,其所有成员访问也必须是常量表达式;否则即使对象本身是
constexpr,取某个成员也可能触发运行时行为(比如调用非constexpr成员函数)
consteval 比 constexpr 更严格,但不能替代 const
consteval 是 C++20 引入的,它强制函数**只能**在编译期求值,连运行时调用都不允许。但它和 const 完全不是同一维度的东西 —— 前者是函数约束,后者是对象/变量的只读性声明。
性能影响:用 consteval 写的函数会被内联展开并彻底常量化,没有运行时开销;但滥用会导致编译变慢、错误信息难懂。
-
consteval函数里调用非consteval/ 非constexpr函数会直接编译失败 -
const变量无法通过consteval提升为编译期常量;它仍然只是运行时只读对象 - 不要试图用
consteval替代const去保护数据——它不提供运行时安全性,也不影响对象生命周期
编译期计算的边界很窄:它依赖于语言对“常量表达式”的明确定义,而不是程序员直觉中的“这个值看起来是固定的”。任何一步脱出规则,整个链就掉回运行时。










