一、C++11 中的 constexpr
1. 常量表达式与 constexpr 概念
现代 C++ 从 C++11 开始引入 constexpr,旨在将计算任务从运行时转移到编译时。常量表达式是指值在编译期就能确定的表达式,比如字面量或常量初始化。
const int a = 1; // a 是常量表达式
const int b = a + 1; // b 也是
int c = 1;
const int d = c; // d 不是,因为 c 的值可能在运行前被修改
编译器会对常量表达式进行优化,直接替换为数值。通过汇编对比可以发现,使用常量表达式的代码在运行时不再包含计算逻辑。
constexpr 关键字用于指定变量必须是常量表达式。它修饰的变量必须用常量或常量表达式初始化,否则编译报错。
注意:
const分为顶层 const(对象本身不可变)和底层 const(指向的对象不可变)。指针被 const 修饰时,*左侧为底层 const,右侧为顶层 const。
2. constexpr 修饰函数
C++11 中,constexpr 函数默认是 inline 的,但限制极其严格:
- 参数和返回值必须是字面类型(整型、浮点型、指针等)。
- 函数体只能有一条
return语句。 - 不能有局部变量、循环或条件分支。
- 必须有返回值且为常量表达式。
只有当结果被 constexpr 变量接收时,才会触发编译期计算;否则退化为普通函数调用。
constexpr int func1() {
return 1;
}
constexpr int func2(int x) {
return x + 10;
}
int main() {
constexpr int a = func1();
constexpr int b = func2(10);
std::cout << a << std::endl;
std::cout << b << std::endl;
return 0;
}
此外,constexpr 还可用于构造函数、成员函数和模板。构造函数要求所有成员初始化为常量表达式且函数体为空;成员函数自动变为 const 且不能是虚函数。
二、C++14 的进化
C++14 大幅放宽了 constexpr 的限制,使其更接近普通函数:
- 允许声明和初始化局部变量。
- 支持
if、for、while、switch等控制流。 - 允许多条
return语句。 - 返回类型可以是
void或复合类型(如std::array)。
constexpr int func(int n) {
if (n == 1 || n == 2) {
return n;
}
int ret = 1; // 允许局部变量
for (int i = 2; i <= n; i++) {
ret *= i;
}
return ret; // 允许多条 return
}
int main() {
constexpr int a = func(5);
std::cout << a << std::endl;
return 0;
}
三、C++17 的扩展
C++17 进一步模糊了编译时与运行时的界限,核心突破是支持 constexpr Lambda 表达式:
- 捕获列表必须是常量表达式。
- 函数体需满足
constexpr函数的其他要求。
int main() {
constexpr int a = 10;
constexpr auto func = [a](int x) constexpr {
return x * a;
};
constexpr int b = func(5);
std::cout << b << std::endl;
return 0;
}
四、C++20 的革命性更新
C++20 对 constexpr 进行了重大升级,使其成为元编程的核心工具:
- 动态内存分配:支持
new/delete,但分配的内存必须在编译期内释放。这为 STL 容器在编译期的实现提供了可能。 - 异常处理:支持
try-catch,但异常必须在编译期捕获,不能传播到运行时。 - mutable 成员:允许在
constexpr成员函数中修改标记为mutable的成员变量。 - 虚函数:首次支持
constexpr虚函数,实现了编译期的多态调用。
尽管 C++23 已发布,但部分编译器支持仍在完善中。随着标准的演进,constexpr 将继续提升 C++ 的性能上限与表达能力。


