const仅表示不可修改,不保证编译期常量;constexpr才强制编译期可求值,用于模板参数、数组维度等需常量表达式的场景。
const 只表示“不可修改”,但它的值可能在运行时才确定。比如:const int x = rand(); 是合法的(只要 x 在作用域内不被修改),但 x 显然不是编译时常量,不能用在需要常量表达式的地方。
常见错误现象:把 const int N = 10; 当成能当数组长度用的“真常量”,结果在 C++98/03 中可能失败(取决于是否为字面量初始化);C++11 起放宽了部分限制,但仍要看初始化方式。
const int a = 42; 或 const int b = foo();,且 foo() 是 constexpr),则 a、b 是“字面类型 + 常量初始化”,可作常量表达式(但需注意上下文)int n = 5; const int c = n;),c 就只是只读变量,不能用于模板非类型参数、switch case、数组维度等const int* p 和 int const* p 等价,但 int* const
p 是指针本身 const —— 这和 constexpr 无关,纯属 const 修饰位置问题constexpr 是更严格的契约:它要求变量或函数必须能在编译期计算出结果,且只能依赖编译期已知的值。一旦违反(比如调用了非 constexpr 函数、用了 new、有未定义行为),编译器直接报错。
使用场景包括:模板参数、std::array 大小、if constexpr 分支、静态断言等所有需要“常量表达式”的地方。
constexpr 变量隐含 const,但 const 变量不隐含 constexpr
constexpr 函数允许更宽松的函数体(如局部变量、循环、条件分支),只要所有可能执行路径都满足编译期可求值std::string 不是字面类型,所以 constexpr std::string s = "hi"; 非法(C++20 前)int global = 42;constexpr int f1() { return 100; } const int f2() { return 200; } // ❌ 错误:const 不能修饰函数(这是语法错误,仅作对比示意)
int main() { const int a = 5; // OK,但 a 不一定是编译时常量 constexpr int b = a; // ❌ 错误:a 不是 constexpr(即使值是 5,但未声明为 constexpr) constexpr int c = 5; // ✅ 正确 constexpr int d = f1(); // ✅ 正确:f1 是 constexpr 函数 // constexpr int e = global; // ❌ 错误:global 是运行时变量 // int arr[a]; // C++98/03 中非法;C++11 起若 a 是“核心常量表达式”才允许(此处 a 不是 constexpr,不一定行) int arr[c]; // ✅ 安全:c 是 constexpr
return 0;}
容易被忽略的细节:const 成员函数 vs constexpr 成员函数
类中声明
void foo() const表示不修改对象状态;而constexpr void bar() const不仅要求bar是 const 成员函数,还要求其整个执行逻辑可在编译期完成,且所在类必须是字面类型(有平凡析构、所有非静态成员都是字面类型等)。这意味着:即使一个
const成员函数逻辑简单,只要它调用了非constexpr的其他函数,或者访问了mutable成员(哪怕没改),就不能标为constexpr。
constexpr 构造函数必须初始化所有成员,且每个初始化器都必须是常量表达式constexpr 支持更多运行时操作(如动态内存分配),但实际编译器支持程度不一,不能默认依赖constexpr 变量、函数或类型,而不是怀疑语法