-3

Consider the following piece of code:

int x = 1;
int y = 2;
y = x + (x = y);

When this runs in C#, the variables end up assigned with these values:

x = 2

y = 3

On the other hand, when the same runs in C++, the variables end like this:

x = 2

y = 4

Clearly, the C++ compiler is using different precedence, associativity and order of evaluation rules than C# (as explained in this Eric Lippert's article).

So, the question is:

Is it possible to rewrite the assignment statement to force C++ to evaluate the same as C# does?

The only restriction is to keep it as a one-liner. I know this can be rewritten by splitting the assignments into two separate lines, but the goal is to maintain it in a single line.

Lesair Valmont
  • 796
  • 1
  • 8
  • 15
  • Out of curiosity is this some kind of assignment you have or requirement, because if it is, it sounds crazy :) – kuskmen Jul 18 '18 at 12:50
  • 2
    It's undefined behaviour. Simplest fix is to breakup into 2 lines of code. See also __what not to ask__: https://stackoverflow.com/questions/24853/what-is-the-difference-between-i-and-i – Richard Critten Jul 18 '18 at 12:50
  • 7
    Drawing parallels between two unrelated languages might prove counter-productive. – Ron Jul 18 '18 at 12:50
  • 1
    Why is there this restriction? Is this just out of interest and not a real-world problem? – EdChum Jul 18 '18 at 12:51
  • @RichardCritten why it is undefined behavior? Expression is evaluated right to left in one case and left to right in the other case, seems pretty defined to me, am I missing something? – kuskmen Jul 18 '18 at 12:52
  • @kuskmen modifying and reading in the same expression. This changes between C++11 and C++17 but left to right etc has nothing to do with expression evaluation and the freedom the compiler is allowed to optimise. Complete answer is here: https://en.cppreference.com/w/cpp/language/eval_order – Richard Critten Jul 18 '18 at 12:54
  • 1
    Anything can be a one-liner if you use enough semi-colons ;) – Ben Jones Jul 18 '18 at 13:00
  • 1
    Sure: `x = y; y = x + x;` – NathanOliver Jul 18 '18 at 13:01
  • 1
    @NathanOliver: doesn't that still result in x=2 & y=4 - OP wanted x=2, y=3 – PaulF Jul 18 '18 at 13:05
  • 2
    @PaulF Oh wow, didn't notice that. I'll have to put my thinking cap on but that doesn't look possible without an extra variable – NathanOliver Jul 18 '18 at 13:06
  • 1
    Why is the GOAL to "maintain it in a single line"? – Peter Jul 18 '18 at 13:51
  • 1
    @Peter: Probably because it is a puzzle or challenge, not a real-world engineering problem. – Eric Lippert Jul 18 '18 at 17:58
  • @Peter (and all other folks that also asked this): Because I am pursuing academic purposes with this exercise. I am trying to achieve an advantageous use of C++ (as language, and resources consumption). – Lesair Valmont Jul 22 '18 at 13:54

3 Answers3

4

Yes, it is possible indeed.

x = (y+=x) - x;

So simple.

hosza
  • 54
  • 1
  • 2
  • I want to clarify that I marked this as the accepted answer because it correctly addresses the question being asked and it provides the most optimal solution so far. – Lesair Valmont Jul 22 '18 at 13:40
3

Is it possible to rewrite the assignment statement to force C++ to evaluate the same as C# does?

Yes. To clarify, the rule in C# is that most of the time, the left side of an expression is evaluated fully before the right side. So C# guarantees that the evaluation of x on the left of the + happens before the side effect of the x = y on the right side. C++ does not make this guarantee; compilers can disagree as to the meaning of this expression.

(Challenge: I said "most" for a reason. Give an example of a simple expression in C# where a side effect on the left is executed after a side effect on the right.)

I know this can be rewritten by splitting the assignments into two separate lines

Right. You wish to have a statement that has two side effects: increment y by the original value of x, and assign the original value of y to x. So you could write that as

int t = y;
y = y + x;
x = t;

But:

the goal is to maintain it in a single line.

I assume by "line" you mean "statement". Fine. Just use the comma operator.

int t = ((t = y), (y = y + x), (x = t));

Easy peasy. Why you would want to, I don't know. I assume this is a puzzle designed to elicit signal on whether or not you know about the comma operator.

Extra credit: How many of the parentheses in that statement are required?


Super bonus: what if we don't want to use the comma operator? We can use other operators as sequencing operators.

int t = ((t = y) & 0) || ((y = y + x) & 0) || (x = t);

We execute the left of the ||, and it turns out to be false, so we execute the right side, and so on.

Similarly, ((expr1) & 0) ? 0 : (expr2) guarantees you that expr1 runs before expr2.

Basically what you need here is a guarantee that a number of subexpressions happen in a particular order. Look up "sequence points" to see which operators in C and C++ produce sequence points; you can use them to sequence expressions, hence the name.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • @NathanOliver: Whoops! Fixed. Thanks. – Eric Lippert Jul 18 '18 at 18:59
  • Not bad but unfortunately you introduced an unused variable warning if you never use `t` (not really a big deal but I always use `-Wall -pedantic`). – NathanOliver Jul 18 '18 at 19:02
  • @NathanOliver: That's a strange warning since plainly the variable is both writtten to and read from! – Eric Lippert Jul 18 '18 at 19:03
  • Huh, I didn't actually read the warning, turns out it was *warning: operation on 't' may be undefined [-Wsequence-point]*. Looks like it might just be a false positive. – NathanOliver Jul 18 '18 at 19:04
  • 1
    @NathanOliver: Well I admit I do not have the C sequence point spec memorized but I was under the impression that comma was a guaranteed sequence operation. – Eric Lippert Jul 18 '18 at 19:06
  • @NathanOliver: I'll try a different version that doesn't use the comma operator. – Eric Lippert Jul 18 '18 at 19:12
  • 1
    The comma operator does introduce a sequence point. The issue (at least what I think gcc is complaining about) is that `t` isn't actually initialized until the end of the expression. I would think that `(t = y)` would make it okay. Only gcc complains so I'm thinking it is just a false positive. – NathanOliver Jul 18 '18 at 19:13
  • @NathanOliver: On the one hand, this is probably a false positive for the reasons your describe. On the other hand, I can't think of any scenario where initializing a variable inside of an assignment to that variable is actually necessary for either performance or readability. – Brian Jul 19 '18 at 13:26
  • 1
    @Eric Lippert: I want to clarify that I upvoted this answer because it provides the best explanation and context-wise references for further learning and exploration of these topics. – Lesair Valmont Jul 22 '18 at 13:38
1

You could write:

int x = 1;
int y = 2;
y = std::exchange(x,y) + y;

But highly unrecommended. You even have to stop browsing and think about this easy operation.

phön
  • 1,215
  • 8
  • 20