Nia*_*all 14 c++ language-lawyer c++11
大多数人都熟悉C++中的"未定义"和"未指定"的行为记录,但是"不需要诊断"呢?
我注意到这个问题和答案,涉及形成不良的程序,但没有详细说明"无需诊断"声明的根源.
委员会在将某些内容归类为"无需诊断"时采用的一般方法是什么?
"未定义"和"未指定"行为的例子并非短缺; 在没有ODR的情况下,"无需诊断"类型错误有哪些实际例子?
Naw*_*waz 12
我会尝试解释分类为未定义行为(UB)的行为"无需诊断".
标准通过说"UB不需要诊断" 1,使编译器完全自由地优化代码,因为编译器只能通过假设您的程序完全定义来消除许多开销(这意味着您的程序没有UB) )这是一个很好的假设 - 毕竟如果这个假设是错误的,那么编译器基于这个(错误的)假设所做的任何事情都会以一种未定义的(即不可预测的)方式运行,这种方式是完全一致的,因为你的程序还是有不确定的行为!
请注意,包含UB的程序可以像任何行为一样自由.再次注意,我说"一致",因为它符合标准的立场:"语言规范和编译器都不保证程序的行为,如果它包含UB(s)".
1. 相反的是"需要诊断",这意味着编译器需要通过发出警告或错误消息向程序员提供诊断.换句话说,它不允许承担程序明确定义,以便优化的代码的某些部分.
这是一篇文章(在LLVM博客上),它使用示例进一步解释了这一点:
除了文章(斜体矿):
有符号整数溢出:如果'int'类型的算术(例如)溢出,则结果是未定义的.一个例子是"INT_MAX + 1"不能保证是INT_MIN.此行为允许某些类别的优化对某些代码很重要.例如,知道未定义INT_MAX + 1允许将"X + 1> X"优化为"真".知道乘法"不能"溢出(因为这样做将是未定义的)允许将"X*2/2"优化为"X".虽然这些看似微不足道,但这些事情通常都是通过内联和宏观扩展来揭示的.这允许的更重要的优化是"<="循环,如下所示:
Run Code Online (Sandbox Code Playgroud)for (i = 0; i <= N; ++i) { ... }在这个循环中,如果"i"在溢出时未定义,编译器可以假设循环将完全迭代N + 1次,这允许广泛的循环优化启动.另一方面,如果变量被定义为在溢出时回绕,然后编译器必须假定循环可能是无限的(如果N是INT_MAX则会发生) - 然后禁用这些重要的循环优化.这特别影响64位平台,因为很多代码使用"int"作为归纳变量.
值得注意的是,无符号溢出保证定义为2的补码(换行)溢出,因此您可以始终使用它们.定义有符号整数溢出的成本是这些优化简单地丢失了(例如,常见的症状是64位目标上的循环内部的大量符号扩展).Clang和GCC都接受"-fwrapv"标志,该标志强制编译器按照定义处理有符号整数溢出(除了INT_MIN除以-1).
我建议你阅读整篇文章 - 它有三个部分,都很好.
希望有所帮助.
Joh*_*itb 12
这里有一个讨论:https://groups.google.com/a/isocpp.org/forum/#!topic/ std- discussion / lk1qAvCiviY,各个委员会成员的发言.
普遍的共识似乎是
正如我在那个帖子中所说,我曾经在一次讨论中听过(我不记得在哪一个,但我确信有相关的有见地的委员会成员)
对我来说粗略的指南是; 如果它在编译时,它往往是"格式错误;不需要诊断",如果它在运行时,它总是"未定义的行为".