Jan*_*tke 1 c++ language-lawyer constant-expression c++23 assumption
在 C++23 中,该[[assume(conditonal-expression)]]属性使得如果条件表达式的计算结果不为true,则行为未定义。例如:
int div(int x, int y) {
[[assume(y == 1)]];
return x / y;
}
Run Code Online (Sandbox Code Playgroud)
y这会编译成与始终相同的代码1。
div(int, int):
mov eax, edi
ret
Run Code Online (Sandbox Code Playgroud)
正如评论者所指出的,这不是必需的优化;这正是 GCC 碰巧处理的信息,除了y == 1UB 之外什么都没有。
编译器完全忽略所有假设是有效的。
编译器需要诊断常量表达式1)中所有未定义的行为,但这合理吗?例如:
constexpr bool extremely_complicated(int x) {
bool result;
// 10,000 lines of math ...
return result;
}
constexpr int div(int x, int y) {
// This should result in a compiler error when the compiler is unable to prove
// what extremely_complicated(x) returns.
// extremely_complicated(x) is not evaluated, so it needs to be able to
// prove the result without evaluating the function.
[[assume(extremely_complicated(x))]];
return x / y;
}
constexpr int quotient = div(4, 2);
Run Code Online (Sandbox Code Playgroud)
即使编译器无法证明假设是否会计算为,这仍然是一个问题吗?true?显然它不能解决停机问题。
假设和常量表达式究竟如何相互作用?[dcl.attr.assume]对此没有任何措辞。
1) 注意:extremely_complicated(x)不是常量表达式,但它位于一个假设中,该假设失败将导致 UB 位于 的常量求值内div(4, 2),这是一个常量表达式。一般认为,UB持续表达就需要诊断。
最终的 C++23 草案assume中有一个特定的例外。
[expr.const]/5.8(参考文献已破译)
表达式
E是核心常量表达式E,除非 的计算遵循抽象机 ( ) 的规则[intro.execution],将计算以下其中一项:
- ...
- 具有
[intro]通过[cpp]、排除中指定的未定义行为的操作[dcl.attr.assume];
因此编译器不需要为了判断表达式的恒定性而判断假设的准确性。如果你写
constexpr int g() {
[[assume(false)]];
return 5;
}
Run Code Online (Sandbox Code Playgroud)
theng()可能是也可能不是核心常量表达式(如果上下文中允许两者并且不返回,则未指定是否[[assume(E)]];取消表达式为常量的资格)。如果你进一步写Econstexprtrue
int main() {
constexpr int x = g();
}
Run Code Online (Sandbox Code Playgroud)
有两种情况。如果实现已确定g()不是核心常量表达式(因为它可以自由地这样做),则需要给出诊断。如果实现已确定g()是核心常量表达式,则程序具有未定义的行为。
所以你看到编译器已经被淘汰了。上下文中的错误假设constexpr可能是未定义的行为,而不是实现选择的诊断。实现可以选择从不检查常量表达式中的假设。如果一个假设被证明是错误的,那么看似成功编译和运行的程序(如果不正确)只是所产生的未定义行为的表现。
| 归档时间: |
|
| 查看次数: |
212 次 |
| 最近记录: |