constexpr
什么是constexpr?
constexpr 表示"这个值(或函数)可以在编译期计算出来"。它不是"常量",而是" 编译时可确定的表达式"。
constexpr引入的原因
在 C++ 中,有些地方必须用编译期常量,比如:
- 数组大小:
int arr[N];→N必须是编译期已知 - 模板参数:
std::array<int, N>→N必须是常量表达式 switch的 case 值等
早期只能用 #define 或 enum,但它们不安全、没类型。
于是 C++11 引入了 constexpr ------ 让普通函数和变量也能参与编译期计算。
constexpr 的演进(各版本增强)
| C++ 版本 | constexpr 能力 |
|---|---|
| C++11 | 只能用于简单变量和极简函数(不能有循环、分支、局部变量等) |
| C++14 | 放宽限制:允许循环、条件、局部变量、多个 return 等(接近普通函数) |
| C++17 | 新增 if constexpr(编译期 if),彻底改变模板编程 |
| C++20 | 几乎所有函数都能 constexpr(包括虚函数、动态内存 new/delete、异常等) |
constexpr vs const ------ 关键区别
| 特性 | const |
constexpr |
|---|---|---|
| 含义 | "运行时常量"(一旦初始化就不能改) | "编译时常量"(必须在编译期算出) |
| 初始化时机 | 运行时(如函数内) | 编译时 |
| 能否用于数组大小? | 一般不行 | 可以 |
| 编译时优化 | 可能优化 | 肯定优化(值直接替换) |
const :承诺运行时 不会变
constexpr :承诺编译时就能算出来
关键点 :所有constexpr都是const,但不是所有const都是constexpr。constexpr是更严格的const,加了"必须在编译时确定"的要求。
C++17 的革命性特性 --if constexpr
c++17之前,模板代码必须为所有类型都能编译
cpp
template<typename T>
void print(T value) {
// 下面的代码必须对所有T类型都有效
if (std::is_pointer<T>::value) {
std::cout << *value; // 如果T不是指针,这行编译错误!
} else {
std::cout << value;
}
}
即使程序逻辑上不会执行*value,编译器也必须检查语法正确性!
c++17 if constexpr编译时决定哪些代码被编译
cpp
template<typename T>
void print(T value) {
if constexpr (std::is_pointer_v<T>) {
// 只有T是指针时,这整块代码才会被编译
std::cout << *value; // 安全!编译器知道T一定是指针
} else {
// 只有T不是指针时,这整块代码才会被编译
std::cout << value;
}
}
与传统if的区别
| 特性 | 普通 if | if constexpr |
|---|---|---|
| 检查时机 | 运行时 | 编译时 |
| 所有分支都要编译吗? | 都要语法检查 | 只编译符合条件的 |
| 能用于不同类型返回吗? | 不可以 | 可以 |
| 能用于函数签名吗? | 不可以 | 可以 |