std::is_constant_evaluated() 仅在编译期求值路径中返回 true,如 constexpr 变量初始化或 consteval 函数内;运行时调用恒为 false,且不可用于 if constexpr 条件。

std::is_constant_evaluated 什么时候返回 true
std::is_constant_evaluated() 只在编译期求值(consteval 函数、constexpr 函数内被常量求值路径调用)时返回 true;运行时调用它永远返回 false。它不是检测“当前是否在 constexpr 上下文”,而是检测“本次函数调用是否正被编译器当作常量表达式求值”。
常见误判场景:在普通 constexpr 函数里调用它,但该函数被运行时变量触发——此时仍走运行时路径,std::is_constant_evaluated() 返回 false,哪怕函数签名带 constexpr。
- ✅ 正确触发
true:constexpr int x = foo();(foo内部调用std::is_constant_evaluated()) - ❌ 不触发
true:int y = foo();即使foo是constexpr,只要 y 非 constexpr,整个调用按运行时处理 - ⚠️ consteval 函数里强制常量求值,所以里面调用一定返回
true,但consteval函数本身不能被运行时调用,否则编译失败
和 if constexpr 混用容易出错
std::is_constant_evaluated() 不能直接放进 if constexpr 条件里——因为它的返回值不是编译期常量表达式(它本身是运行时可调用函数),这么写会编译报错:error: 'std::is_constant_evaluated()' is not a constant expression。
正确做法是用普通 if,靠编译器对分支的死代码消除(dead code elimination)来裁剪:编译期路径只保留 true 分支,运行期路径只保留 false 分支。
立即学习“C++免费学习笔记(深入)”;
- ❌ 错误:
if constexpr (std::is_constant_evaluated()) { ... } - ✅ 正确:
if (std::is_constant_evaluated()) { ... } else { ... } - ? 编译器能优化掉不可达分支,但要求两个分支都语法合法(比如不能在
true分支里写std::vector构造,因为 constexpr 环境不支持)
典型用途:同一函数兼顾 constexpr 和运行时逻辑
最实用的场景是封装一个函数,让它在编译期走轻量逻辑(比如查表、位运算),运行期走完整逻辑(比如调用 std::sqrt、分配内存)。避免为同一语义写两个函数(constexpr_foo + foo)。
例如实现一个安全的整数平方根:编译期用二分查找(仅限小整数),运行期调用 std::sqrt。
constexpr int safe_isqrt(int n) {
if (std::is_constant_evaluated()) {
// 编译期:只处理 [0, 255],避免递归或循环展开爆炸
if (n < 0) return 0;
int r = 0;
while ((r+1)*(r+1) <= n) ++r;
return r;
} else {
// 运行期:交给标准库,支持大数和浮点转换
return static_cast<int>(std::sqrt(static_cast<double>(n)));
}
}
- 注意:编译期分支里的循环必须能在编译期完成(n 要足够小,且循环次数可静态推导)
- 若编译期逻辑抛异常(如
throw)或调用非 constexpr 函数,会导致编译失败 - 运行期分支里的代码完全不受 constexpr 限制,但整个函数仍需声明为
constexpr才能被编译期调用
替代方案少,但要注意 MSVC 的早期版本兼容性
在 C++20 中,std::is_constant_evaluated() 是唯一标准方式。C++17 或更早只能靠宏或重载模拟,但不可靠。
MSVC 从 19.28(VS 2019 16.8)才完整支持;旧版可能静默返回 false,或在某些 consteval 场景下行为异常。Clang 11+、GCC 10+ 支持较稳。
- 检查方法:
static_assert(__cpp_lib_is_constant_evaluated >= 201811L); - 跨平台项目建议加构建时检测,而不是只靠
__cplusplus - 它不改变函数是否 constexpr,只是提供运行时“感知”能力——这点容易和
consteval混淆
new 或 std::string),这时编译器不会报“分支不可达”,而是直接报 constexpr 违规——得倒回去检查调用链上每个实参是不是真 constexpr。


















