constexpr变量必须编译期求值,const变量仅运行时不可修改;前者用于模板参数和数组大小,后者不满足编译期常量要求,且可能占用运行时内存。

constexpr变量必须在编译期求值,const变量只保证运行时不可修改
这是最根本的分水岭。一个constexpr变量不只是“不能改”,它要求整个初始化表达式必须能在编译阶段完全计算出确定结果;而const只要求变量初始化后不被赋值,哪怕这个初始化发生在main()里调用某个函数返回值,也完全合法。
常见错误现象:const int n = rand();能编译通过,但你没法用n去定义std::array<int n></int>或作为模板非类型参数——编译器会报错“non-type template argument is not a constant expression”。
-
constexpr变量隐含const,但const变量不隐含constexpr - 只有字面类型(literal type)才能声明为
constexpr,比如std::string在C++20前就不行 - 初始化表达式中若出现
new、dynamic_cast、未定义行为,或调用了非constexpr函数,constexpr声明直接失败
数组大小和模板参数只能接受constexpr,不能退而求其次用const
当你写int arr[N];或std::array<int n> a;</int>时,N必须是常量表达式(constant expression)。C++标准明确要求:这不看变量是不是const,而看它是否满足编译期可求值约束。
示例对比:
const int a = 10; // ✅ 合法,但a只是运行时常量 constexpr int b = 10; // ✅ 合法,b是编译期常量 constexpr int c = a + 5; // ✅ C++11起,若a是字面量初始化的const,c仍可推导为constexpr const int d = b + 3; // ✅ d也是const,且因b是constexpr,d也被视为常量表达式(用于数组等场景) int arr1[a]; // ❌ C++14之前非法;C++17起若a是常量表达式才允许,但a本身不是constexpr,依赖编译器放宽程度 int arr2[b]; // ✅ 始终合法
- C++11起,部分
const整型变量若以字面量初始化(如const int x = 42;),可被当作常量表达式使用,但这属于“隐式常量折叠”,不可靠、不跨平台、不适用于复杂类型 - 模板非类型参数(如
template<int n> struct X {};</int>)只认constexpr,const再“纯”也不行
constexpr函数在编译期调用时才真正发挥价值
constexpr修饰函数,不是说“这个函数一定在编译期运行”,而是说“它有能力在编译期运行”——前提是所有实参都是常量表达式。否则,它就退化为普通函数,在运行时执行。
例如:
constexpr int square(int x) { return x * x; }
constexpr int s1 = square(5); // ✅ 编译期计算,s1是constexpr
int x = 7;
int s2 = square(x); // ✅ 运行时调用,s2只是普通int
- 函数体受严格限制:C++11要求单条return语句;C++14起允许循环、if、局部变量,但所有分支都必须可编译期求值
- 返回类型和参数类型必须是字面类型;不能有虚函数、非public非static数据成员等
- 别指望
constexpr函数自动优化所有调用——只有传入编译期已知值时,才触发编译期求值
const变量占内存地址,constexpr变量可能完全不占运行时内存
const变量哪怕值是42,也会分配存储空间(通常在.rodata段),取值需访存;而constexpr变量在多数情况下会被编译器直接常量折叠,变成指令里的立即数,零运行时开销。
验证方式:查看生成的汇编,constexpr int x = 42;后续用到x的地方大概率直接是mov eax, 42;而const int y = 42;可能表现为mov eax, DWORD PTR y[rip]。
- 这不是规范保证,而是优化行为——但它是
constexpr设计的本意,也是性能差异的物理来源 - 对嵌入式或高频调用场景,这种区别直接影响L1缓存命中率和指令吞吐
- 调试时注意:
constexpr变量可能在调试器里“看不到”,因为它根本没分配变量名对应的内存位置
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











