const仅保证运行时只读,不强制编译期求值;constexpr要求初始化器为编译期常量表达式,用于模板参数、数组大小等场景。

const 变量声明不保证编译期求值
写 const int x = 42; 看似简单,但它只是告诉编译器“x 初始化后不能被修改”,并不强制要求 42 这个值必须在编译期就确定。如果换成 const int y = get_value();(get_value() 是普通函数),只要 y 不用于需要常量表达式的地方,代码依然合法——y 是运行时常量,仅具只读性。
常见错误现象:const int n = 5; std::array<int n> arr;</int> 在某些旧编译器或 C++11 前标准下可能报错;更典型的是 const double d = 3.14; int buf[d]; 直接失败,因为 double 类型的 const 不参与整型常量表达式的历史兼容规则。
-
const可用于任何类型,包括类类型、指针、引用 - 初始化器可以是运行期表达式(如函数调用、变量运算)
- 不能直接作为模板非类型参数(如
std::array<t n></t>中的N)
constexpr 变量必须满足常量表达式约束
constexpr 不是“更严格的 const”,而是引入了新语义:该变量的初始化器必须是常量表达式,且类型需支持字面值(literal type)。写 constexpr int x = 42; 合法;但 constexpr int y = rand(); 编译直接失败,因为 rand() 不是常量表达式。
使用场景集中在需要编译期确定值的地方:数组大小、模板实参、static_assert 条件、switch 的 case 标签。
- 初始化器必须是编译期可求值的表达式(字面量、
constexpr函数调用、其他constexpr变量等) - 类型必须是字面值类型(基本类型、有
constexpr构造函数的类等) - 声明即定义,不能分离声明与定义(如不能先
extern constexpr int x;再定义)
函数前加 const 还是 constexpr?含义完全不同
const 修饰成员函数(如 int get() const)表示该函数不修改对象状态,它修饰的是隐含的 this 指针,和“常量值”无关;而 constexpr 修饰函数(如 constexpr int square(int x))表示该函数体满足编译期可求值条件,且当实参是常量表达式时,结果也必为常量表达式。
容易踩的坑:constexpr 函数在 C++14 后允许循环、局部变量等,但若传入非常量参数(如普通变量),它仍会退化为运行时调用——语法上没报错,但失去了编译期计算的意义。
-
const成员函数不能出现在类外声明中(只能在类内或类定义后加const) -
constexpr函数可独立于类存在,也可作为静态成员或非静态成员,但构造函数、析构函数需显式标记才能是constexpr - C++20 起,
constexpr函数可含new/delete、虚函数调用等,但仅限于编译期上下文可用的子集
混合使用时,constexpr 已隐含 const
constexpr int x = 42; 等价于 const /* literal type */ int x = 42;,所以你不能再写 constexpr const int x = 42;——重复修饰无意义,多数编译器会警告或忽略后者。
真正需要注意的是:当你想让一个对象既运行时只读、又能在编译期参与计算,应优先选 constexpr;只有当值确实只能运行期获得(比如配置文件读取、系统时间),才用 const。
性能影响很实际:用 constexpr 计算数组长度,编译器可完全消除边界检查;用 const 就只能靠运行时优化,且未必能推导出长度信息。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











