Why chained prefix increment/decrement for builtin type is not UB for C++?
Asked Answered
B

2

9

In cpprefernce.com example for prefix increment there is such code:

int n1 = 1;
...
int n3 = ++ ++n1;

Why chained increment in this case does not lead to UB? Is rule for at most once modified not violated in this case?

Bernice answered 17/2, 2016 at 18:28 Comment(0)
L
11

In C++11 and later, UB occurs when there are two writes or a write and a read that are unsequenced and access the same memory location. But ++x is equivalent to x+=1, so ++ ++n1 is equivalent to (n1+=1)+=1, and here the reads and writes happen in a strict sequence because of the properties of assignment and compound assignment operators: first n1 is read, then one plus the original value is written, then the resulting value is read again, then one plus that value is written back.

In C++03, this was UB, because of the old rule you allude to: there is no sequence point between the two modifications. But in C++11 there are no longer any sequence points; instead there is the "sequenced before" partial order.

Lassa answered 17/2, 2016 at 18:34 Comment(1)
It became way too complicatedBernice
K
2

See Brian's answer for simpler terms, .

It's legal cause the C++ standard says so, emphasis are mine... Going by the C++14 draft

5.3.2 Increment and decrement [expr.pre.incr]

The operand of prefix ++ is modified by adding 1, or set to true if it is bool (this use is deprecated). The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type or a pointer to a completely-defined object type. The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field. If x is not of type bool, the expression ++x is equivalent to x+=1

So, this is perfectly legal

#include <iostream>
using namespace std;

int main(){
    int x =8;
    int y = ++ ++ ++ ++ ++ ++ ++ ++ x;
    cout << x << " " << y;

}

Output

16 16

1.9 Program execution [intro.execution]

15...If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. ...

And it's attached example:

void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
Kyser answered 17/2, 2016 at 18:33 Comment(4)
"See Brian's answer for the actual answer"... sorry, I couldn't resist.Prakrit
I did not ask if that is syntactically correct, I asked if that leads to UB, which is completely different.Bernice
I quoted the standard with respect to the operators themselves, "...the result is an lvalue..." basically has one part of the answer there...Kyser
Well, I've since updated it. I didn't see Brian's answer earlier, that was why I modified my answer to acknowledge hisKyser

© 2022 - 2024 — McMap. All rights reserved.