C++17 排序:post-赋值左侧递增

C++17 sequencing: post-increment on left side of assignment

C++17 标准通过规则修改了 C++ 语言的操作顺序定义 stating, to the effect:

In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1

然而,当使用 -std=c++17-Wall

在 GCC 8.1 中编译以下代码时
int v[] { 0,1,2,3,4,5,6,7 };
int *p0 = &v[0];
*p0++ = *p0 + 1;
cout << "v[0]: " << v[0] << endl;

我收到以下警告:

main.cpp:266:8: warning: operation on 'p0' may be undefined [-Wsequence-point]
     *p0++ = *p0 + 1;
      ~~^~

输出为:

v[0]: 1

问题是:警告有误吗?

And the question: is the warning erroneous?

视情况而定。

从技术上讲,相关代码定义明确。在 C++17 中,右侧先于左侧排序,而之前是不确定排序。 gcc 正确编译代码,v[0] == 1 在赋值后。

然而,这也是不应该编写的可怕代码,所以虽然警告的具体措辞是错误的,但警告的实际精神对我来说似乎很好。至少,我不会提交关于它的错误报告,而且它似乎不值得开发人员花时间修复。 YMMV.

[我在下面留下我的答案以供参考,但 表明我在下面的答案不完整,其结论最终是不正确的。]

C++17 标准(草案here),[expr.ass],的确如此:

The right operand [of an assignment operator] is sequenced before the left operand.

这对我和你来说都是错误的。 @Barry 不喜欢你的示例代码,所以为了避免分散问题的注意力,我测试了替代代码:

#include <iostream>

namespace {
    int a {3};

    int& left()
    {
        std::cout << "in left () ...\n";
        return ++a;
    }

    int right()
    {
        std::cout << "in right() ...\n";
        return a *= 2;
    }
}

int main()
{
    left() = right();
    std::cout << a << "\n";
    return 0;
}

输出(使用 GCC 6.3):

in left () ...
in right() ...
8

无论您是考虑打印的消息还是考虑 8 的计算值,它看起来好像 left 操作数在 right[=41= 之前排序] 操作数——这是有道理的,就高效的机器代码而言

  • 通常应该更愿意决定在哪里存储计算结果
  • 在实际计算结果之前。

我不同意@Barry。您可能发现了标准的一个重要问题。有空的时候报告一下。

更新

@SombreroChicken 补充道:

That's just because GCC 6.3 didn't correctly implement C++17 yet. From 7.1 and onwards it evaluates right first as seen here.

输出:

in right() ...
in left () ...
6