std::is_constant_evaluated() 仅在 constexpr 函数中被编译器明确作为常量表达式求值时返回 true,否则编译失败;必须配合 if constexpr 分支,且两分支需各自满足其上下文约束。

std::is_constant_evaluated() 不是运行时性能开关,它只在被明确要求常量求值的调用点返回 true;写错分支内容或用错上下文,函数直接编译失败。
必须在 constexpr 函数里调用,否则连编译都过不去
这个函数根本不能出现在普通函数中。GCC 和 Clang 会直接报 call to non-constexpr function 'std::is_constant_evaluated'。哪怕你只写一行 return std::is_constant_evaluated();,也得给整个函数加上 constexpr 修饰。
常见错误:
- 在非 constexpr 函数里试图“探测当前是不是编译期”——语法不合法
- 在 lambda 里调用,但 lambda 捕获了非常量变量或调用了非 constexpr 函数——该 lambda 就无法参与常量求值
- 在 C++17 或更早标准下使用——符号未定义,链接失败
调用点决定真假,不是函数声明或参数类型决定的
std::is_constant_evaluated() 返回什么,只看“这次调用是否被编译器当作常量表达式求值”,和函数有没有 constexpr、参数是不是 const int 都无关。
立即学习“C++免费学习笔记(深入)”;
有效触发 true 的场景只有这些:
static_assert(my_func(42) == 17);constexpr int x = my_func(42);-
int arr[my_func(42)];(数组大小) -
template<int n> struct X {};</int>+X<my_func> y;</my_func>(NTTP 推导) - 在
consteval函数体内调用(必然为true)
无效场景(一律返回 false):
-
int x = my_func(42);(普通运行时调用) -
const int a = 42; my_func(a);(a是 const,但不是字面量或 constexpr 变量) -
my_func(argv[1]);(运行时输入)
if constexpr 才是唯一安全的分支写法
用普通 if 会导致编译器检查两个分支的合法性。只要 else 分支里有 std::sqrt、new、std::cout 这类运行时专属操作,整个函数就无法用于 static_assert 或模板参数——哪怕你永远不走那个分支。
正确写法必须是:
constexpr int parse_version(const char* s) {
if constexpr (std::is_constant_evaluated()) {
// 编译期分支:只用字面量、循环展开、<code>static_assert</code>、<code>std::string_view</code>
int major = 0, minor = 0;
for (int i = 0; s[i] && s[i] != '.'; ++i)
major = major * 10 + (s[i] - '0');
return major * 100;
} else {
// 运行时分支:可用 <code>std::strtol</code>、<code>std::vector</code>、系统调用等
return std::strtol(s, nullptr, 10) * 100;
}
}关键点:
-
if constexpr让编译器只实例化进入的分支,另一分支不参与语义检查 - 编译期分支里禁止任何非常量操作:不能调用
std::strlen,不能取非常量全局变量地址,不能用dynamic_cast - 运行时分支虽自由,但它的存在本身会影响函数能否参与常量求值——所以别在里面写调试输出或日志
跨编译器行为差异大,验证只靠 static_assert
Clang 更保守,通常只在 static_assert、NTTP、consteval 等明确场景才返回 true;GCC 12+ 在 constinit 初始化中可能更激进地尝试常量求值,导致行为不一致。
唯一可靠验证方式:
- 写
static_assert(my_func(42) == expected);,能过说明这次调用确实在编译期完成 - 如果失败,
std::is_constant_evaluated()就是false,别猜原因,先检查调用点 - 头文件中定义带
std::is_constant_evaluated()的函数要格外小心——ODR 和模板实例化时机可能让行为不可预测
最易被忽略的一点:它不剪枝、不优化、不加速,只是个信号。你得自己把两套逻辑都写对,且各自满足其上下文约束——编译期分支写错一句,整个 constexpr 路径就崩,而运行时调用却可能完全正常。


















