C++里的const和constexpr有什么区别?(编译时常量与运行时常量)

11次阅读

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

C++里的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),则 ab 是“字面类型 + 常量初始化”,可作常量表达式(但需注意上下文)
  • 若用运行时值初始化(如 int n = 5; const int c = n;),c 就只是只读变量,不能用于模板非类型参数、switch case、数组维度等
  • 指针/引用,const int* pint 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 变量、函数或类型,而不是怀疑语法

text=ZqhQzanResources