当年我们写 constexpr。
经常会发生一种误会。
你以为它一定在编译期算。
其实它只是“允许”。
允许和保证。
差很多。
当年:constexpr 不是承诺
你写了 constexpr。
编译器可能在编译期算。
也可能不算。
特别是当参数来自运行时的时候。
你根本没法强迫它。
线上啪一下:本该编译期报错的东西,跑到了线上才炸
你写了一个检查。
希望非法配置在编译期就被拒绝。
结果它变成了运行时分支。
线上才走到那条路。
才触发错误。
consteval:必须在编译期
consteval 就是把话说死。
“你必须在编译期调用我。”
consteval int must_be_const(int x) {
return x * 2;
}
constexpr int a = must_be_const(21);
这能过。
但你如果这样写。
int x = 21;
int b = must_be_const(x);
就会编译失败。
它逼你早点暴露问题。
constinit:我不保证编译期值,但保证初始化是静态的
有些问题不是“算不算”。
是“初始化顺序”。
老 C++ 有个经典坑。
静态初始化顺序灾难。
你在不同编译单元里。
放了全局对象。
谁先构造。
不稳定。
constinit 的意思是。
这个变量必须被常量初始化。
这样它就不会落进“运行时动态初始化”。
constinit int g_port = 8080;
它不是 constexpr 变量。
但它保证初始化不会乱跑。
is_constant_evaluated:我现在是不是在编译期
有些代码你希望。
编译期走一条路。
运行时走另一条。
这时你可以问一句。
#include <type_traits>
constexpr int f(int x) {
if (std::is_constant_evaluated()) {
return x + 1;
}
return x + 2;
}
这句问话。
不是给你写花活。
它更像一个工具。
让你在需要的时候。
能区分这两个世界。
关键结论
constexpr 是“可以”。
consteval 是“必须”。
constinit 是“别给我搞初始化顺序”。
小结
把编译期说清楚。
事故会少很多。