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

2

4

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?

Isborne answered 11/10, 2018 at 14:22 Comment(4)
Of course the standard indicates that *p0 + 1 will be evaluated before anything on the left. Since the ++ is postfix, whether it gets evaluated or not does not effect the assignment to v[0]. The real question is whether p0 now points to v[1].Cattleya
No warning hereOctillion
@Octillion You have to add -WallArsphenamine
@Octillion you have warnings off. Add -WallIsborne
A
9

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.

Arsphenamine answered 11/10, 2018 at 15:49 Comment(1)
Of course, "terrible code" is often in the eye of the beholder. Some think that A && B || C && D is terrible code because they never studied formal logic that and didn't "grow up" implicitly knowing that conjunctions always have higher precedence that disjunctions. Others think (A && B) || (C && D) is terrible code because the use of parentheses suggests non-standard evaluation order. The point being that that expression feels like terrible code because it's ingrained habit of mind from decades of C++ coding that's it's UB. But now it's not. So what once terrible is now elegant syntax.Isborne
F
1

[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
Funny answered 27/3, 2019 at 21:11 Comment(4)
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 : wandbox.org/permlink/6OtnRcfOaxivYfN2Forewoman
@Funny "You may have discovered a nontrivial problem with the standard," I think you mean with the compiler?Isborne
@SombreroChicken You seem to watch [C++] closely, so I suspect that you might have already seen it, but in case you have not: your illuminating comment and test run have now provoked a related question and answers: In the expression left() = right(), why is right() sequenced first?Funny
@Funny yes I saw that. Great question and answers there.Isborne

© 2022 - 2024 — McMap. All rights reserved.