C++ 专家(19):constexpr 一切——编译期计算、constexpr 容器、consteval
更新时间:2026-09-01。本文是
languages/cpp/expert/专家层第 19 篇,接 协程机制深入。C++11 引入 constexpr 后在 C++14/17/20 中不断扩展,现在已经支持在编译期使用 vector、string、算法。C++20 的 consteval 和 constinit 进一步细化了编译期编程。
本文要回答的问题
- constexpr 函数的编译期求值条件是什么?
- C++20 的 constexpr vector 和 string 怎么用?
- consteval 和 constinit 分别是什么?
- 编译期字符串哈希、查找表、元编程怎么做?
一、constexpr 函数求值条件
cpp
// constexpr 函数可以在编译期或运行期求值
constexpr int square(int x) {
return x * x;
}
// 编译期求值
constexpr int a = square(5); // 编译期计算
// 运行期求值
int b = 5;
int c = square(b); // 运行期计算
// 如果所有参数都是常量表达式,则编译期求值
// 如果参数是运行时变量,则运行期求值二、constexpr vector 和 string(C++20)
cpp
// C++20 中 vector 和 string 可以在 constexpr 中使用
constexpr int process() {
std::vector<int> v = {1, 2, 3};
v.push_back(4);
v.push_back(5);
int sum = 0;
for (auto x : v) {
sum += x;
}
return sum; // 15
}
static_assert(process() == 15);
constexpr std::string make_greeting(const char* name) {
std::string result = "Hello, ";
result += name;
result += "!";
return result;
}
static_assert(make_greeting("world") == "Hello, world!");限制:
- 不能在 constexpr 中动态分配后泄漏(C++20 检查)
- 不能在 constexpr 中用虚函数(C++20 之前)
- 不能在 constexpr 中用 try/catch(C++20 之前)
三、consteval(C++20)
cpp
// consteval:强制编译期求值
// 如果不能在编译期求值,编译错误
consteval int compile_time_only(int x) {
return x * x;
}
constexpr int a = compile_time_only(5); // OK
// int b = compile_time_only(5); // 也 OK,编译期求值
// int x = 5;
// int c = compile_time_only(x); // ❌ 编译错误:x 不是常量表达式
// 用途:编译期字符串哈希
consteval uint32_t hash(const char* str) {
uint32_t h = 0;
while (*str) {
h = h * 31 + *str++;
}
return h;
}
// 编译期计算哈希
constexpr uint32_t h = hash("hello");
// 对比 switch 常量
switch (hash("command")) {
case hash("start"): break;
case hash("stop"): break;
case hash("reset"): break;
}四、constinit(C++20)
cpp
// constinit:保证变量在静态初始化期初始化
// 比 constexpr 更宽松,不需要编译期常量
constinit std::vector<int> v = {1, 2, 3}; // 静态初始化,非编译期
// 和 constexpr 的区别
constexpr std::vector<int> cv = {1, 2, 3}; // ❌ constexpr 变量不能是动态类型
constinit std::vector<int> cv2 = {1, 2, 3}; // ✅ constinit 可以
// constinit 保证没有静态初始化顺序问题
// 保证在 main 之前初始化完成五、编译期查找表
cpp
// 编译期生成查找表
template<size_t N>
struct LookupTable {
std::array<int, N> data;
constexpr LookupTable() {
for (size_t i = 0; i < N; i++) {
data[i] = i * i; // 平方表
}
}
};
// 编译期计算
constexpr auto table = LookupTable<256>();
static_assert(table.data[5] == 25);
static_assert(table.data[10] == 100);六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| constexpr 函数不保证编译期 | 可能运行期执行 | 用 consteval 强制编译期 |
| constexpr 容器内存泄漏检查 | 编译错误 | 确保 constexpr 中无内存泄漏 |
| constexpr 虚函数 | 编译错误 | 用 CRTP 替代虚函数 |
| constinit 和 constexpr 混用 | 语义混淆 | constinit 用于静态初始化,constexpr 用于编译期常量 |
相关与延伸
下一篇:编译期计算——模板元编程、类型列表、编译期函数;constexpr 入门,见 C++17/20 新特性。
一句话总结
C++ constexpr 一切:C++20 支持 constexpr vector/string,编译期可以 push_back、累加、拼接;consteval 强制编译期求值,适合编译期哈希、代码生成;constinit 保证静态初始化期初始化,避免顺序问题;编译期查找表用 constexpr array 在编译期生成数据,运行时零开销。