const仅表示不可修改,不保证编译期常量;constexpr才强制编译期可求值,用于模板参数、数组维度等需常量表达式的场景。

const 不一定代表编译期常量
const 只表示“不可修改”,但它的值可能在运行时才确定。比如:const int x = rand(); 是合法的(只要 x 在作用域内不被修改),但 x 显然不是编译时常量,不能用在需要常量表达式的地方。
常见错误现象:把 const int N = 10; 当成能当数组长度用的“真常量”,结果在 C++98/03 中可能失败(取决于是否为字面量初始化);C++11 起放宽了部分限制,但仍要看初始化方式。
- 若用字面量或 constexpr 表达式初始化(如
const int a = 42;或const int b = foo();,且foo()是constexpr),则a、b是“字面类型 + 常量初始化”,可作常量表达式(但需注意上下文) - 若用运行时值初始化(如
int n = 5; const int c = n;),c就只是只读变量,不能用于模板非类型参数、switchcase、数组维度等 - 对指针/引用,
const int* p和int const* p等价,但int* const p是指针本身 const —— 这和constexpr无关,纯属const修饰位置问题
constexpr 强制要求编译期可求值
constexpr 是更严格的契约:它要求变量或函数必须能在编译期计算出结果,且只能依赖编译期已知的值。一旦违反(比如调用了非 constexpr 函数、用了 new、有未定义行为),编译器直接报错。
使用场景包括:模板参数、std::array 大小、if constexpr 分支、静态断言等所有需要“常量表达式”的地方。
立即学习“C++免费学习笔记(深入)”;
-
constexpr变量隐含const,但const变量不隐含constexpr - C++14 起,
constexpr函数允许更宽松的函数体(如局部变量、循环、条件分支),只要所有可能执行路径都满足编译期可求值 - 注意返回类型和参数类型必须是字面类型(literal type),例如
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构造函数必须初始化所有成员,且每个初始化器都必须是常量表达式 - 从 C++20 开始,
constexpr支持更多运行时操作(如动态内存分配),但实际编译器支持程度不一,不能默认依赖 - 调试时如果遇到 “not a constant expression” 错误,优先检查是否混用了非
constexpr变量、函数或类型,而不是怀疑语法









