__builtin_is_constant_evaluated
是用于在clang和gcc的标准库中实现std::is_constant_evaluated
的内置函数。
在恒定上下文中无效的代码通常也使优化程序难以恒定折叠。
例如:
int f(int i) {
if (__builtin_is_constant_evaluated())
return 1;
else {
int* ptr = new int(1);
int i = *ptr;
delete ptr;
return i;
}
}
{p>由gcc -O3
发出为:
f(int):
sub rsp, 8
mov edi, 4
call operator new(unsigned long)
mov esi, 4
mov rdi, rax
call operator delete(void*, unsigned long)
mov eax, 1
add rsp, 8
ret
因此优化程序使用了__builtin_is_constant_evaluated() == 0
clang将其折叠为常数,但这是因为clang的优化器可以删除不需要的动态分配,而不是因为它使用了__builtin_is_constant_evaluated() == 1
。
我知道这将定义__builtin_is_constant_evaluated()
实现的返回值,因为优化因一个编译器而异。但是只有在两个路径具有相同的可观察行为时,才应该使用is_constant_evaluated。
为什么优化器无法折叠,为什么它不使用__builtin_is_constant_evaluated() == 1
并退回到__builtin_is_constant_evaluated() == 0
呢?
答案 0 :(得分:5)
constexpr bool is_constant_evaluated() noexcept;
返回:
true
当且仅当调用的评估发生在对表达式或转换的评估明显地发生时 常量评估([expr.const])。
f
永远不能在常量求值的表达式或转换中调用,因此std::is_constant_evaluated()
返回false
。这是由编译器决定的,与优化器无关。
当然,如果优化器可以证明分支是等效的,则它可以进行恒定折叠。但这毕竟是优化-超出了C ++语言本身的范围。
但是为什么会这样呢?引入了std::is_constant_evaluated
的提案是P0595。它很好地解释了这个想法:
constexpr double power(double b, int x) { if (std::is_constant_evaluated() && x >= 0) { // A constant-evaluation context: Use a // constexpr-friendly algorithm. double r = 1.0, p = b; unsigned u = (unsigned)x; while (u != 0) { if (u & 1) r *= p; u /= 2; p *= p; } return r; } else { // Let the code generator figure it out. return std::pow(b, (double)x); } } // ... double thousand() { return power(10.0, 3); // (3) }
[...]
Call(3)是核心常量表达式,但实现不是 需要在编译时对其进行评估。因此,我们指定它 使
std::is_constant_evaluated()
产生false
。它的 试图将其指定为true
或false
在这种情况下,但这引起了重大的语义问题: 这样一来,答案可能会在各个阶段间变得不一致 汇编。例如:int *p, *invalid; constexpr bool is_valid() { return std::is_constant_evaluated() ? true : p != invalid; } constexpr int get() { return is_valid() ? *p : abort(); }
此示例尝试依靠constexpr评估这一事实 检测未定义的行为以避免对constexpr不友好的调用
abort()
在编译时。但是,如果std::is_constant_evaluated()
可以返回true
,我们现在遇到了这样一种情况: 运行时检查被绕过。