意外的评估顺序(编译器错误?)

可能重复:
未定义的行为和序列点

我不确定这是不是一个海湾合作委员会的错误,所以我会问:

unsigned int n = 0;
std::cout << n++ << n << ++n;

海湾合作委员会给出了非常奇怪的结果:“122”AFAICT是不可能的。 因为<<是左联合的,所以它应该和下面的一样:

operator<<(operator<<(operator<<(std::cout, n++), n), ++n)

并且因为在评估参数之前和之后都有一个序列点,所以在两个序列点之间n永远不会修改两次(甚至是访问) - 所以它不应该是未定义的行为,只是未指定评估的顺序。

所以AFAICT的有效结果是:111 012 002 101

没有别的


评估参数和调用函数之间有一个序列点。 评估不同参数之间没有顺序点。

让我们看看最外层的函数调用:

operator<<(operator<<(operator<<(std::cout, n++), n), ++n)

论据是

  • operator<<(operator<<(std::cout, n++), n)
  • ++n
  • 没有具体说明哪些是首先评估的。 当第二个参数被评估时,也允许第一个参数被部分评估。

    从标准的[intro.execution]部分(3225草案的措词):

  • 如果A在B之前未被测序并且B在A之前未被测序,则A和B不被测序。 [注意:不确定评估的执行可能会重叠。 - 结束注释]

  • 除非另有说明,否则对个别操作符和个别表达式的操作数的评估是不确定的。 [注意:在一个程序执行期间多次评估的表达式中,对其子表达式的无序和不定序评估不需要在不同的评估中一致地执行。 - 结束符]运算符结果的值计算之前,运算符操作数的值计算被排序。 如果对标量对象的副作用不是相对于同一标量对象的另一个副作用或使用同一标量对象的值进行值计算而言是未定义的,则行为是未定义的。

  • 因为在相同的标量对象上有多个带有副作用的操作,这些操作相互之间没有序列关系,所以你处于未定义行为的范围,甚至999也是允许的输出。


    编译器错误的第一条规则:它可能不是一个编译器错误,而是你的误解。 在同一个语句中使用后缀和前缀运算符会导致未定义的行为。 尝试使用-Wall选项为您提供更多警告并向您展示代码中的潜在缺陷。

    让我们看看GCC 4.2.1在询问有关test.cpp警告时告诉我们什么:

    #include <iostream>
    
    int main() {
        unsigned int n = 0;
        std::cout << n++ << n << ++n << std::endl;
        return 0;
    }
    

    当我们编译时:

    $ g++ -Wall test.cpp -o test
    test.cpp: In function ‘int main()’:
    test.cpp:5: warning: operation on ‘n’ may be undefined
    

    您的代码它为什么在一些书的话是有经验的程序员不喜欢这样的例子(++, - )操作符重载,甚至是其他语言(红宝石)没有实施++或- 。

    链接地址: http://www.djcxy.com/p/73235.html

    上一篇: Unexpected order of evaluation (compiler bug?)

    下一篇: Twisted C++ code