ThomasMcLeod
ThomasMcLeod

Reputation: 7769

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

The C++17 standard revised the definitions of the order of operations for the C++ language by a rule 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

However, when compiling the following code in GCC 8.1 with -std=c++17 and -Wall

int v[] { 0,1,2,3,4,5,6,7 };
int *p0 = &v[0];
*p0++ = *p0 + 1;
cout << "v[0]: " << v[0] << endl;

I get the following warning:

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

The output is:

v[0]: 1

And the question: is the warning erroneous?

Upvotes: 4

Views: 1528

Answers (2)

thb
thb

Reputation: 14454

[I leave my answer below for reference but further discussion has shown that my answer below is incomplete and that its conclusion is ultimately incorrect.]

The C++17 standard (draft here), [expr.ass], indeed reads:

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

This sounds as wrong to me as it does to you. @Barry dislikes your sample code so, to avoid distracting the question, I have tested alternate code:

#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;
}

Output (using GCC 6.3):

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

Whether you regard the printed messages or consider the computed value of 8, it looks as though the left operand were sequenced before the right operand—which makes sense, insofar as efficient machine code

  • should generally prefer to decide where to store a computed result
  • before actually computing the result.

I would disagree with @Barry. You may have discovered a nontrivial problem with the standard. When you have some time, report it.

UPDATE

@SombreroChicken adds:

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.

Output:

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

Upvotes: 1

Barry
Barry

Reputation: 303517

And the question: is the warning erroneous?

It depends.

Technically, the code in question is well-defined. The right-hand side is sequenced before the left-hand side in C++17, whereas before it was indeterminately sequenced. And gcc compiles the code correctly, v[0] == 1 after that assignment.

However, it is also terrible code that should not be written, so while the specific wording of the warning is erroneous, the actual spirit of the warning seems fine to me. At least, I'm not about to file a bug report about it and it doesn't seem like the kind of thing that's worth developer time to fix. YMMV.

Upvotes: 9

Related Questions