std::is_constant_evaluated() 仅在被常量表达式上下文强制求值时返回 true,如 static_assert 或数组大小;普通调用、变量传参或低版本 C++ 中均无效,且必须配合 if constexpr 使用。

std::is_constant_evaluated() 总是返回 false?检查调用点是否强制常量求值
它不看函数有没有 constexpr 修饰,只看“这次调用”是不是被上下文明确要求编译期结果。比如:static_assert(compute(5) == 36) 或 int arr[compute(5)] = {}; 才可能触发 true;而 int x = compute(5); 即使 compute 是 constexpr 函数,也必然走运行时分支,std::is_constant_evaluated() 返回 false。
常见错误现象:
- 在普通函数里调用
std::is_constant_evaluated()→ 编译失败(“call to non-constexpr function”) - 用变量传参:如
int a = 5; compute(a);→ 永远false,哪怕a是const int - 在 C++17 或更低标准下使用 → 符号未定义,链接失败
if constexpr (std::is_constant_evaluated()) 是唯一合法写法
普通 if 会让编译器检查两个分支的合法性,哪怕条件恒为 true。一旦运行时分支里有 std::sqrt、new 等非 constexpr 操作,整个函数就无法用于常量表达式上下文。
必须用 if constexpr,这样编译器只实例化进入的分支,另一分支不参与语义检查:
立即学习“C++免费学习笔记(深入)”;
constexpr int parse_version(const char* s) {
if constexpr (std::is_constant_evaluated()) {
// 编译期:只能用字面量、<code>std::string_view</code>、循环展开、<code>static_assert</code>
int major = 0, minor = 0;
for (int i = 0; s[i] && s[i] != '.'; ++i)
major = major * 10 + (s[i] - '0');
for (int i = 0; s[i]; ++i)
if (s[i] == '.') {
++i;
while (s[i] && s[i] >= '0' && s[i] <= '9')
minor = minor * 10 + (s[i++] - '0');
break;
}
return major * 100 + minor;
} else {
// 运行期:可调用 <code>std::strtol</code>、<code>std::sscanf</code>、分配临时缓冲区
return std::strtol(s, nullptr, 10) * 100 + std::strtol(s + 2, nullptr, 10);
}
}Clang 和 GCC 对“隐式常量求值”的判断尺度不同
GCC 12+ 在 constinit 初始化或某些 const 变量赋值中更激进地尝试常量求值,可能导致 std::is_constant_evaluated() 返回 true;Clang 更保守,通常只在 static_assert、模板非类型参数、consteval 调用等明确场景才返回 true。
跨平台项目应以 Clang 行为为底线,验证方式唯一可靠:
- 用
static_assert包裹调用,通过即说明该次执行确实在编译期完成 - 避免依赖
const int x = f();这类模糊上下文,它在 Clang 下大概率不触发常量求值 - 若需强保证,改用
consteval函数,但注意它不能用于运行时路径
编译期分支写错一个操作,整条路径就废掉
编译期分支不是“能跑就行”,而是每个子表达式都必须满足 constexpr 约束。哪怕只是多写了一个 std::printf、对非常量全局变量取地址、或调用未标记 constexpr 的成员函数,都会导致编译失败。
容易被忽略的细节:
-
std::array构造合法,std::vector在 C++23 前非法;C++23 允许但会显著拖慢编译 - 递归深度无编译器统一限制,GCC 可能允许 512 层,Clang 默认 256,超限报错而非静默截断
- 分支内调用的辅助函数也必须是
constexpr,且不能间接引入非constexpr依赖 - 别指望编译器帮你“剪枝”——只要语法上存在非法操作,不管是否可达,编译就挂


















