Thursday, February 3, 2022

[SOLVED] std::exchange working differently with VC++ and gcc

Issue

following code:

#include <utility>

int main()
{
  auto pos = 0;
  auto rel = pos - std::exchange(pos, pos + 1);

  return rel; // g++: 0, VC++: 1
}

If you try the code on rextester with the VC++ compiler then the result is 1, with gcc on godbolt the result is 0 (results are apparently not returned with gcc using rextester).

Question: Why are the results different?

And 2nd question: Are there any tools to check for that mistake? Any clang warnings?

My guess is that std::exchange in VC++ is called before the other operand is evaluated, whereas in gcc this is not the case. If you swap the operands pos and std::exchange the result is -1 (or 255) both VC++ and with gcc.

It is probably something about side effects - and calling std::exchange which clearly has a side-effect.

Fortunately I caught the bug with a unit test after transitioning from VC++ to gcc - and was a bit flustered at first and boiled it down to this simple (not) working example.


Solution

Binary operator - is note associated with a sequence point, meaning it is not specified in which order expressions A and B will be evaluated in A - B:

Consider two functions f() and g(). In C and C++, the + operator is not associated with a sequence point, and therefore in the expression f()+g() it is possible that either f() or g() will be executed first. [...] In C and C++, evaluating such an expression yields undefined behavior.[

Thus, your program have undefined behaviour, and any analysis of cross-compiler behaviour is futile.


In standardese, this is [intro.execution]/17 [emphasis mine]:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations.  — end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a memory location is unsequenced relative to either another side effect on the same memory location or a value computation using the value of any object in the same memory location, and they are not potentially concurrent, the behavior is undefined. [ Note: The next section imposes similar, but more complex restrictions on potentially concurrent computations.  — end note ]

[ Example:

void g(int i) {
  i = 7, i++, i++;  // i becomes 9

  i = i++ + 1;      // the value of i is incremented
  i = i++ + i;      // the behavior is undefined
  i = i + 1;        // the value of i is incremented
}

 — end example ]



Answered By - dfrib
Answer Checked By - Marie Seifert (WPSolving Admin)