58

While browsing the code of some project I came across the following statement:

++x %= 10;

Is this statement well defined in C++ or does it fall into the same category as

a[i] = i++

?

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
kyku
  • 5,892
  • 4
  • 43
  • 51
  • Yes, it's undefined. It also won't compile under C. – Sneftel Jan 08 '15 at 08:33
  • More to explore: http://stackoverflow.com/a/4176333/157247 – T.J. Crowder Jan 08 '15 at 08:46
  • 1
    @Falco: LOL! The latter. – T.J. Crowder Jan 08 '15 at 10:34
  • @T.J.Crowder good! I want to implement the proposed solution without any mistakes ;-) – Falco Jan 08 '15 at 10:38
  • 1
    @paxdiablo: Pre-sequencing (in the time of sequence points), you might have been right in C++ with it being undefined. Since C++11, you aren't anymore. – Deduplicator Jan 08 '15 at 10:39
  • 3
    So, @Dedup, you're going to _cite_ the relevant reference, yes? Because, as far as I can tell for C++11, it's _still_ undefined. I don't mind being corrected if I'm wrong but only by a reference to the actual standard somewhere :-) – paxdiablo Jan 08 '15 at 10:42
  • 1
    Added an answer with the relevant quote. It's simple, and well-defined. – Deduplicator Jan 08 '15 at 11:09
  • 24
    Close the project and burn it with fire! –  Jan 08 '15 at 11:56
  • 3
    You can vote on what it should do: http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evaluation/ – Thomas W. Jan 08 '15 at 12:29
  • Note that the code is equivalent to `++x = x % 10`. Not very different from `i = i++;` or other such standard examples of UB. – Lundin Jan 08 '15 at 15:50
  • Lundin: I say it's equivalent to "++x" by itself, and that "i = i++" has no effect. The fact that there are legitimate arguments for both is the reason it is undefined. – Random832 Jan 08 '15 at 16:41
  • 1
    @Lightness: If you really feel that way, go ask on Meta to burninate [tag:language-lawyer], as it's basically all questions like this one. – Kevin Jan 08 '15 at 20:05
  • 1
    @Kevin: Most of my 170 questions are [tag:language-lawyer] and they are _nothing_ like this one. Anyway, a cursory glance through the list of questions under that tag shows that you are completely wrong. – Lightness Races in Orbit Jan 08 '15 at 20:09
  • 1
    @Lightness: I suppose you're right. Nevertheless, OP didn't write this code, so don't blame them. – Kevin Jan 08 '15 at 20:24
  • @Kevin: OP re-asked the question. – Lightness Races in Orbit Jan 08 '15 at 20:29
  • 1
    @Light: Then dupe it. – Kevin Jan 08 '15 at 20:32
  • 11
    I feel sorry for you having to deal with this code. Is the original author still employed somewhere? I would like to avoid any products he/she is associated with. This kind of "syntax" is begging for mistakes. Don't even ask if it's legal. Change it to readable code - make the intention unambiguously clear. – Floris Jan 08 '15 at 23:57
  • 2
    @Floris: I would guess OP is going to do that just as soon as (s)he knows what the code actually does. Also, some employers will not let you fix things like this for fear of introducing bugs in an already rickety system. – Kevin Jan 09 '15 at 02:27
  • @Kevin: exactly. first the point of the question is finding out what it does. but the best way because UB is a possibility here (I vote for UB with 90% chance, even in C++11); he should execute it on the compiler the original guy wrote it on, because the product was tested in this state. and take what code was generated and write it clearly. – v.oddou Jan 09 '15 at 06:07
  • Can someone explain to me what possible situation ++x could actually be used as an lvalue _without_ being undefined behavior? I don't understand why this is a valid expression at all in C++. – Random832 Jan 09 '15 at 16:58
  • The code (apart from whether or not it works) seems to increment x by 1, clipping at 10. So putting it in a loop will effectively count from 0 to 9, then restarting at 0. – aross Jan 12 '15 at 09:52
  • @aross, as per my answer, so will `x = (x + 1) % 10` but _without_ any confusion or "language-lawyer" discussion :-) I much prefer that to the one in the question. – paxdiablo Jan 13 '15 at 13:43
  • @Random, as pointed out by Deduplicator at various points below, the result of `++x` is the lvalue `x` following the update. That's vastly different to (for example) `x + 1` where the result is not an lvalue at all. – paxdiablo Jan 13 '15 at 13:47
  • @paxdiablo Why is it an lvalue? What is the benefit of having it be an lvalue, when you can't assign to it without being undefined behavior? I assumed this expression required a class with unusual operator overloading to be valid because it makes no sense with e.g. an int. – Random832 Jan 13 '15 at 17:07
  • @random, the contention is that it _isn't_ UB, and that's supported by the standard as per the many comments here. If you believe otherwise, that's fine but you'll need to cite the standard to convince anyone :-) – paxdiablo Jan 13 '15 at 22:30
  • This looks the same as `++++x` or `(x+=1)+=1` which are obviously valid... The fact that those operators return a non-const lvalue is a dead giveaway. With `int`, sure, `x=(x+1)%10` is fine, but for an expensive type, in-place operations are often cheaper, and splitting the code into `++x; x%=10;` may not seem necessary (from the reactions here, it appears that it is if there is a chance that someone else will inherit your code; so be it). – Marc Glisse Jan 17 '15 at 21:18

7 Answers7

64

As per C++11 1.9 Program execution /15:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

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.

In this case, I believe ++x is a side effect and x %= 10 is a value computation so you'd think it would be undefined behaviour. However, the assignment section (5.17 /1) has this to say (my bold):

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

Hence that means that both sides are sequenced before the assignment and before the result of the assignment is made available. And since the standard also states (5.17 /7) that x OP = y is identical to x = x OP y but with x only being evaluated once, it turns out this is well defined behaviour, as it's equivalent to:

++x = Q % 10; // where Q is the value from ++x, not evaluated again.

The only question then remains is which side of the assignment is evaluated since they're not sequenced. However, I don't think it matters in this case since both these will have the same effect:

++x = Q % 10; // LHS evaluated first
Q = ++x % 10; // RHS evaluated first

Now, that's my reading of the standard. While I have a fair amount of experience in decoding complex documents, there may be something I've missed - I don't think so because we've all had a lively discussion here in getting to this point :-) and I think we've all established the relevant sections.

But, regardless of whether it's wel defined or not, decent coders shouldn't be writing code like that. It's been a long time since the low-memory/small-storage days of the PDP minis, it's about time we wrote our code to be readable.

If you want to increment then take the modulo, use x = (x + 1) % 10, if only to make it easier to understand for the next poor Joe reading that code.

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 2
    Actually, you quote a good one. But it's not enough by far, as in `++x` the modification of `x` is sequenced before the value of the expression. – Deduplicator Jan 08 '15 at 10:41
  • 1
    @Dedup, where is your _reference_ for that? `1.9` states that, unless noted, it's unsequenced. And there's nothing i can see in `5.3.2 Unary incr/decr` that specifies sequencing. Searching from 5.3, the word sequenced doesn't show up until two sections _after_ unary incr/decr. – paxdiablo Jan 08 '15 at 10:46
  • I'm not trying to start an argument but, unless your last name is Stroustrup, I'm unlikely to take what you say as gospel :-) You'll need to _show_ me why I'm wrong. – paxdiablo Jan 08 '15 at 11:00
  • 2
    Take a look at the definition of unary ++ and --. There's absolutely no trouble with sequencing, everything is well-defined. – Deduplicator Jan 08 '15 at 11:05
  • Deduplicator is correct. And your answer is incorrect. There are no side-effects in `++x` that might be done after the lvalue-to-rvalue conversion, so the second modification is sequenced in respect to the first. – Columbo Jan 08 '15 at 11:07
  • 1
    @paxdiablo: But here's the rub, from 5.3.2: `If x is not of type bool, the expression ++x is equivalent to x+=1`. And this is where sequencing *within this sub-expression* becomes well-defined (the `x = x + 1`). Of course, with the example in OP's question, due to the indeterminate sequencing in the rest of the full-expression, it's a moot point. You can have well-defined sub-expressions combined in a way within a full-expression that, given *indeterminate sequencing* of the sub-expressions will *on the whole* render the complete full-expression UDB. – frasnian Jan 08 '15 at 11:18
  • Maybe I'm not explaining it well enough but it's right there in the text I quoted. **Except when noted,** they're unsequenced. If they're unsequenced and there are multiple side effects or a side effect with value modification, it's UB. The fact that no mention is made in the unary++/-- _confirms_ my analysis. There is nothing in unary++/-- or = for that matter that overrides that first paragraph I quote. There's also nothing in `x = x + 1` and the quote explicitly states subexpressions of individual expressions as being unsequenced as well. – paxdiablo Jan 08 '15 at 11:18
  • 1
    For `++lvalue`: "The result is the updated operand; it is an lvalue." How much more explicit do you want it? – Deduplicator Jan 08 '15 at 11:21
  • @paxdiablo Both the right-hand side and the left-hand side of the compound-assignment expression are evaluated before the whole expression is. So **`++x` is evaluated before `++x %= y` is**. I'm sure you don't mean to tell us that `++x` has side-effects that can be postponed until after the evaluation of `++x`? – Columbo Jan 08 '15 at 11:21
  • But precisely that is the case! That is what all this sequencing stuff is about. See 1.9 (7) in C++03 or 1.9 (11) in C++11. – Wintermute Jan 08 '15 at 11:22
  • Actaually I was the "poor Joe" reading that code, and so I decided to ask about it on SO. – kyku Jan 08 '15 at 11:27
  • @Wintermute: Please give the part of the definition of unary ++ which allows for side-effects which would be resolved after the value-computation of the result. – Deduplicator Jan 08 '15 at 11:33
  • 5.2.6 (1) in C++11: "The value computation of the `++` expression is sequenced before the modification of the operand object." – Wintermute Jan 08 '15 at 11:34
  • 1
    @Wintermute: THat's the wrong ++ operator. You looked at postfix, but the other one is the only relevant one. – Deduplicator Jan 08 '15 at 11:35
  • @Dedup, it's not a matter of what unary++ says, it's already been stated in my original quote that it will cause a problem unless the relevant section overrides it. However, on looking at the assignment stuff, that _does_ appear to override it sufficiently. – paxdiablo Jan 08 '15 at 11:35
  • @Wintermute You are reading the wrong paragraphs. For `++x` every modification is done before the value computation. – Columbo Jan 08 '15 at 11:37
  • Technically, the assertion in this answer that "`In this case, ++x is the side effect and x %= 10 is a value computation so it's undefined behaviour.`" seems completely wrong: `++x` is in fact equivalent to `x += 1`, putting it on the same footing as `x %= 1`, which is the whole reason that indeterminate sequencing matters here. `++x` is NOT a side effect - it is a value computation AND a side effect, the same as `LHS %= RHS` is. Determining what `RHS` is in this equation is easy. OTOH, `LHS` is undefined behavior. – frasnian Jan 08 '15 at 11:39
  • So after [expr.ass]/1: "In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.", all side-effects from `++x` - that is, the modification of x' value - are sequenced before the assignment. – Columbo Jan 08 '15 at 11:40
  • Ah. Yes, I slipped a section to the side there. Still, the assignment paragraph is not conclusive because it specifically talks about value computations, and if that is not sequenced in relation to the side effect, there's nothing to go on. – Wintermute Jan 08 '15 at 11:43
  • Bods, you've convinced me tangentially anyway :-) The assignment section explicitly overrides any unsequenced issues. – paxdiablo Jan 08 '15 at 11:45
  • I think I'll take down my answer until I convinced myself one way or another. I have doubts now. – Wintermute Jan 08 '15 at 11:46
  • @T.J.Crowder I added an answer, but I doubt it is gonna sort out the confusion too well. – Columbo Jan 08 '15 at 11:54
  • @Columbo: Actually, never mind -- I had it in my head that `x` appeared on the right-hand side in the awful code, but it doesn't. – T.J. Crowder Jan 08 '15 at 12:10
  • @T.J.Crowder That kind of stuff happens to me all the time :) – Columbo Jan 08 '15 at 12:11
  • Okay, I convinced myself (and the answer stays down). Since `++x` is equivalent to `x += 1`, the assignment operator language in C++11 sequences the side effect before the value computation of `++x`. It is perhaps worth noting that this requirement is new in C++11, though; the expression is undefined in C++03 (where the sequencing language in the assignment operator section is missing). – Wintermute Jan 08 '15 at 12:12
  • @Wintermute That was presumably defected. There is no way that in any version of C++, `x = y` is evaluated before `x` and `y` are. – Columbo Jan 08 '15 at 12:16
  • 2
    But there is the possibility that side effects are delayed. `i = ++i + 1;` is specifically mentioned as undefined at the beginning of section 5 in C++03. A compiler could conceivably generate code for `++x %= 10` that takes `x` into a register, increments it, shifts the result to another register, does the division, moves the result of that into the memory position of `x` and then moves the previously calculated result of the incrementation to that same memory address. The operation is not defined in the C++03 abstract machine, and close to the metal there is a lot of room for surprises. – Wintermute Jan 08 '15 at 12:21
  • 1
    @paxdiablo; What makes you sure that in `++x = Q % 10;`, `Q` is the value from `++x` ? – haccks Jan 08 '15 at 13:53
  • 1
    @haccks The standard states that `a %= b` is equivalent to `a = a % b`, and `a` is only evaluated once. – Columbo Jan 08 '15 at 18:39
  • 1
    I think C++11 lambda syntax can help a bit. `X %= Y` is `mod_assign( [&]()->decltype(auto){return X;}, [&]()->decltype(auto)(return Y;})` where `mod_assign( auto f1, auto f2 ) { auto&& lhs = f1(); auto&& rhs = f2(); lhs = lhs % rhs; }` and the call to `f1()` and `f2()` are unsequenced relative to each other. Then again, maybe this isn't easier. – Yakk - Adam Nevraumont Jan 09 '15 at 18:26
22

TL;DR: It is well-defined because x is guaranteed to be incremented before the assignment.


Some C++11 standardese

[intro.execution]/15:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

However, [expr.ass]/1 does note something:

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

So this does constitute an exception to the first quote. Moreover, as stated in [expr.pre.incr]1, ++x is equivalent to x += 1, which is also covered by the above quote: The assignment is sequenced before the value computation. Thus for ++x the increment is sequenced before the value computation.

Taking that into account it is not hard to see that in ++x %= 10, the increment is done before the assignment is.
So the increment side-effect is sequenced before the assignment side-effect, and thus all involved side-effects are sequenced.

To put it in another way, the standard imposes the following sequencing:

  • ++x and 10 are evaluated - the order of which is unsequenced, but 10 is just a literal so that's not relevant here.
    • ++x is evaluated:
      • First, the value of x is incremented.
      • Then the value computation is done, and we get an lvalue referring to x.
  • The assignment is done. The updated value of x is taken modulo 10 and assigned to x.
  • The value computation of the assignment might follow, which is clearly sequenced after the assignment.

Hence

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.

doesn't apply as the side-effects and value-computations are sequenced.


1 Not [expr.post.incr] which would be for the postfix increment!

Columbo
  • 60,038
  • 8
  • 155
  • 203
20

Let's look at the unary increment operator:

5.3.2 Increment and decrement [expr.pre.incr]

1 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.
[...]

Thus, all evaluations and side-effects pertaining to that unary operator are scheduled before its value, and thus cannot cause havoc.

All that's left is evaluating %= 10 on that lvalue. Only evaluating the constant might be concurrent (which could not possibly do any harm), the rest is strictly sequenced after everything else.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • certainly not. because nothing says "strictly afterwards" because there are 2 assignments in this whole expression and the only true sequence points are the surrounding `;` – v.oddou Jan 09 '15 at 05:40
  • 1
    @v.oddou: You are forgetting the sequencing relationships. And unary increment does all its work (incrementing the operand) before returning the operand itself as an lvalue. – Deduplicator Jan 09 '15 at 11:55
  • yep, you're right. I think I got it now, like I mentioned in a comment on haccks's answer. However this is only true in C++11, C++03 is UB in this case. my first comments applies in 03. – v.oddou Jan 09 '15 at 12:29
6

In the expression

++x %= 10;  

the most confusing part is that x is modifying twice between two sequence points, once by prefix ++ and once by assignment of result. This makes an impression that the above expression invokes undefined behavior as once we learned in old C++ that

Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.

In C++11, the rule is different (it is about sequencing instead of sequence points!):

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.

As we already know that ++x is the above expression will be evaluated only once (and will give a lvalue), because

C++11: 5.17

The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.

and also know that evaluation of operands ++x and 10 will take place before computation of result of %= operator as by standard:

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

Conclusion:

++x will be evaluated only once giving an lvalue and only after that %= operation will be performed. This means that, both modifications to x is sequenced and the above expression is well defined.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • you're taking too much of a shortcut. the value yes but not the side effect. therefore, still unsequenced and still undefined as the left hand side's side effect conflicts with the side effect of `%=` nothing sequences them in the standard. but +1 for mentionning something that appears like a strong C++03 and 11 difference ! – v.oddou Jan 09 '15 at 05:50
  • @v.oddou; `++x` and then `%=` are sequenced. – haccks Jan 09 '15 at 07:18
  • yes after reading some more about that in Joannes Shlaub's answer I now concur that in C++11 only, indeed the **prefix** `++x` is sequenced in its side effect, because the value must be sequenced by standard AND the operator returns a `lvalue`. this is not obvious at all, but now I see it. – v.oddou Jan 09 '15 at 07:24
5

I'm going to offer an alternative answer, without quoting the good book, as I think re-writing it slightly makes it obvious.

++x %= 10;       // as stated
x += 1 %= 10;    // re-write the 'sugared' ++x

This makes it clear enough in my eyes. As we know, the result of the assignment (which if we really want, the still 'sugared' += reduces to) is itself an lvalue, so there should be no doubt that by further reduction the expression is:

(x = x+1) %= 10  // += -> =1+
x = (x+1) % 10   // assignment returns reference to incremented x
OJFord
  • 10,522
  • 8
  • 64
  • 98
  • 3
    no ! this is the correct expansion: `x = (x=x+1) % 10` 2 assignments between sequence points -> undefined – v.oddou Jan 09 '15 at 05:35
  • @v.oddou, there are no sequence _points_ in C++11, it just deals with sequencing of various actions. And, as per the standard, the two sides of an assignment are _completely_ sequenced before the assignment itself happens. Hence the second `=` in your statement is done before the first. -> defined. – paxdiablo Jan 19 '15 at 23:38
  • @paxdiablo I think you are right. but as I find, this is only true for C++11. In C++03 it still happens as I said. – v.oddou Jan 20 '15 at 02:00
4

Well, it is defined. As undefined behavior:

§1.9/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.

There are two unsequenced side-effects here.

Given the expression ++x %= 10; moving from left-to-right, we have:

  1. a value computation (x+1)
  2. modifying an object ( =, as in x = x + 1), e.g. a side effect per §1.9/12
  3. an indeterminately sequenced operation (%=) that itself has both a value computation ('%') and an object-modification side-effect ('=') (ibid from 1,2) on the same scalar object ('x').

The two sub-expressions in the full-expression are unsequenced relative to each other. Although we started by reading it from left to right, these are indeterminately sequenced, so there is explicitly no partial-ordering bailout from §1.9/13:

Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced.

So, UDB.

frasnian
  • 1,973
  • 1
  • 14
  • 24
  • 5
    I was reaching for the downvote button just as I saw you second sentence. Chuckle :-) – paxdiablo Jan 08 '15 at 09:26
  • 1
    Yeah, frankly I'd ditch the first. Remember a lot of people have minimal English. And people don't read, your first sentence on its own is flatly wrong. – T.J. Crowder Jan 08 '15 at 09:28
  • @T.J.Crowder: point taken. Editing first/second sentences to accommodate ESL. – frasnian Jan 08 '15 at 09:33
  • 1
    Take a look at the definition of unary ++ and --. There's absolutely no trouble with sequencing, everything is well-defined. – Deduplicator Jan 08 '15 at 11:07
  • 1
    @Deduplicator: I think this is continuing in another comment discussion so I'll follow it there, but consider the equivalency in 5.3.2 vs. the problem in the `%=` in OP's example case -- your comment is simply not addressing the problem of *exactly what* is on the left side of that `%=` in the context of a full-expression. I'm not necessarily saying you're wrong - I'm saying that your comment *without references* essentially amounts to saying "nothing to see here folks, just move along" – frasnian Jan 08 '15 at 11:31
  • @frasnian He wrote an answer. – Columbo Jan 08 '15 at 11:33
  • @Columbo: I don't doubt that - OP's question is turning into a medusa. I may have missed something DD said while I was responding to something else. (on edit: just saw what I believe you're referring to, will eval/respond there; thanks). – frasnian Jan 08 '15 at 11:45
  • What if one of the operators doesn't actually have the side effect of modifying the value? What if they modify different fields of x's class? If I understand "scalar object" correctly, x cannot be a scalar object because there is no scalar type that has a ++ operator that returns an lvalue. – Random832 Jan 08 '15 at 16:35
  • @Deduplicator: yes, but you fail to see the whole expression is not just one unary operator. frasnian has the good answer of this page. – v.oddou Jan 09 '15 at 05:38
  • @Random832: scalar comes from mathematics, it means "one value" notably from the Reals set of numbers usually. but in computer science it usually means any fundamental type. – v.oddou Jan 09 '15 at 05:44
  • @v.oddou That doesn't answer the question of whether class/struct objects qualify as "scalar objects" in the C++ standard or not. I assume this is a formal term defined elsewhere in the standard. – Random832 Jan 09 '15 at 16:55
  • @Random832: here is your answer. as you can see I wasn't far with the fundamental. except a scalar cannot be void and can be a pointer. http://howardhinnant.github.io/TypeHiearchy.pdf – v.oddou Jan 13 '15 at 02:15
1

The Prefix increment (++x) has highest precedence by the Modulus assignment (%= ). The statement: ++x %= 10; can be expressed as:

++x;
x%= 10;