Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"A quick poll about order of evaluation…"

94 views
Skip to first unread message

Lynn McGuire

unread,
Dec 1, 2014, 3:54:34 PM12/1/14
to
"A quick poll about order of evaluation…"
http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evaluation/

"Consider this program fragment:"

" std::vector<int> v = { 0, 0 };
int i = 0;
v[i++] = i++;
std::cout << v[0] << v[1] << endl; "

"My question is not what it might print under today’s C++ rules. The third line runs afoul of two different categories of undefined
and unspecified behavior."

"Rather, my question is what you would like the result to be. Please let me know."

My answer is 02.

Lynn

Martijn Lievaart

unread,
Dec 1, 2014, 4:10:59 PM12/1/14
to
On Mon, 01 Dec 2014 14:54:07 -0600, Lynn McGuire wrote:

> "Rather, my question is what you would like the result to be. Please let
> me know."

Undefined. It's fine as it is.

M4

Mr Flibble

unread,
Dec 1, 2014, 4:38:56 PM12/1/14
to
I would like the result to remain the same as it is now, i.e. undefined.

/Flibble

David Harmon

unread,
Dec 1, 2014, 5:18:32 PM12/1/14
to
On Mon, 01 Dec 2014 14:54:07 -0600 in comp.lang.c++, Lynn McGuire
<l...@winsim.com> wrote,
>v[i++] = i++;
>
>"My question is not what it might print under today’s C++ rules. The third line runs afoul of two different categories of undefined
>and unspecified behavior."
>
>"Rather, my question is what you would like the result to be.

I would like it to be undefined behavior.

Christopher Pisz

unread,
Dec 1, 2014, 6:51:43 PM12/1/14
to
Does not compile.
If it did, I am not sure what the problem would be.

I expect the output to be 01 and when I rewrote your snippet to a fully
compilable program it indeed was 01.

// Standard Includes
#include <iostream>
#include <vector>


int main()
{
std::vector<int> v;
v.push_back(0);
v.push_back(0);

int i = 0;
v[i++] = i++;

std::cout << v[0] << v[1] << std::endl;

return 0;
}

Is there some undefined behavior here that I am not aware of?




Christopher Pisz

unread,
Dec 1, 2014, 6:53:30 PM12/1/14
to
Derp, I mean 10. 5'o clock dyslexia.



red floyd

unread,
Dec 1, 2014, 7:02:04 PM12/1/14
to
Yes. First of all, i is accessed and modified twice between a sequence
points
*** v[i++] = i++;

I'm not sure what the second category of UB that Herb refers to is.
I thought at first it might be that v[2] was being referenced, but
it's using postincrement.



Christopher Pisz

unread,
Dec 1, 2014, 7:02:43 PM12/1/14
to
Oh, I see, you are getting at the postfix occurring after the statement
rather than after the lhs or rhs of the statement is evaluated. Didn't
know that was undefined and made it 20 years without running into that.
I suppose that's a product of not using postfix in favor of code that
looks like it does what it actually does.





Öö Tiib

unread,
Dec 1, 2014, 7:26:11 PM12/1/14
to
On Monday, 1 December 2014 22:54:34 UTC+2, Lynn McGuire wrote:
> "A quick poll about order of evaluation..."
My answer is Other: the code is ill-formed and diagnostic is required.

JiiPee

unread,
Dec 1, 2014, 8:09:21 PM12/1/14
to
On 01/12/2014 20:54, Lynn McGuire wrote:
I agree with my GCC compiler: 10

Because it should go from left to right like we are reading it. So [i++]
first, then after that i++.

I am interested; why should it be something else than this? We read (and
the compiler as well) the code from top to bottom and from left to right
normally, isnt it?

its the same with if-statement, isnt it?
like:
if( x > 5 || y < 10 )
...

here if x > 5 will be checked first and if its true y < 10 is not
executed at all, isnt it? so its from left to right there as well.

JiiPee

unread,
Dec 1, 2014, 8:25:35 PM12/1/14
to
ok, but then Visual C++ 2008 gives: 00. So obviously if compilers give
different results this should be illegal (meaning there is no standard
rule for it).

Juha Nieminen

unread,
Dec 2, 2014, 4:06:59 AM12/2/14
to
Christopher Pisz <nos...@notanaddress.com> wrote:
>> " std::vector<int> v = { 0, 0 };

> std::vector<int> v;
> v.push_back(0);
> v.push_back(0);

I think starting to use the new standard is ok by now.

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Chris Vine

unread,
Dec 2, 2014, 4:47:49 AM12/2/14
to
It would be equally undefined if you used prefix notation. In either
case you would be modifying the same variable twice without one
modification being sequenced before the other (in C++11/14 speak) or
without an intervening sequence point (in C++98/03 speak).

Chris

Martijn Lievaart

unread,
Dec 2, 2014, 5:45:21 AM12/2/14
to
On Tue, 02 Dec 2014 01:25:17 +0000, JiiPee wrote:

> On 02/12/2014 01:09, JiiPee wrote:
>> On 01/12/2014 20:54, Lynn McGuire wrote:

>> its the same with if-statement, isnt it?
>> like:
>> if( x > 5 || y < 10 )
>> ...

No. || is defined to evaluate from left to right *and* short circuit.

>>
>> here if x > 5 will be checked first and if its true y < 10 is not
>> executed at all, isnt it? so its from left to right there as well.
>
> ok, but then Visual C++ 2008 gives: 00. So obviously if compilers give
> different results this should be illegal (meaning there is no standard
> rule for it).

It is illegal. It's called undefined behaviour.

M4

Juha Nieminen

unread,
Dec 2, 2014, 8:00:58 AM12/2/14
to
Martijn Lievaart <m...@rtij.nl.invlalid> wrote:
>>> if( x > 5 || y < 10 )
>
> No. || is defined to evaluate from left to right *and* short circuit.

Understanding the guarantee of short-cirtuiting of && and || is
important. It's both useful, and can cause unexpected results if you
are not aware.

For example:

if(foo(a) && bar(b)) ...

bar(b) will not be called if foo(a) returns false. If you are not aware
of this, it can come as a surprise and cause malfunction.

OTOH, since it's guaranteed to behave like that, it's handy because
you can now be sure that bar(b) is not called if foo(a) returns false.
This is handy eg. to first check the validity of a parameter and then
do something with the parameter only if it's valid. A typical example
would be something like:

if(i != 0 && a / i > 10) ...

Richard Damon

unread,
Dec 2, 2014, 8:49:41 AM12/2/14
to
On 12/1/14, 8:09 PM, JiiPee wrote:
> On 01/12/2014 20:54, Lynn McGuire wrote:
>> "A quick poll about order of evaluation…"
>> http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evaluation/
>>
>> "Consider this program fragment:"
>>
>> " std::vector<int> v = { 0, 0 };
>> int i = 0;
>> v[i++] = i++;
>> std::cout << v[0] << v[1] << endl; "
>>
>> "My question is not what it might print under today’s C++ rules. The
>> third line runs afoul of two different categories of undefined and
>> unspecified behavior."
>>
>> "Rather, my question is what you would like the result to be. Please
>> let me know."
>>
>> My answer is 02.
>>
>> Lynn
>
> I agree with my GCC compiler: 10
>
> Because it should go from left to right like we are reading it. So [i++]
> first, then after that i++.
>
> I am interested; why should it be something else than this? We read (and
> the compiler as well) the code from top to bottom and from left to right
> normally, isnt it?
>

There is an argument that a = b should evaluate b first, and then
process a to do the assignment. Also, given a + b * c some would say
that the natural order to evaluate things would be b, c, b*c, a, a+b*c.
These sort of expectations make a strict left to right become
"surprising" at times. Yes, some languages will attempt to reduce
undefined behavior by defining an order, inevitably this will sometimes
be surprising to users, and will often force the compiler to generate
less efficient code. One of the guiding principles of C that C++
inherited was that efficiency was important, and the compiler was
allowed to assume that the programmer isn't trying hard to cause
problems (if the programmer isn't following the rules, it is ok to bite
them). This causes C and C++ to have perhaps more undefined behavior
conditions than other languages, but also explains their popularity,
programmers LIKE languages that trust them and generate faster results
because of that. (Sometime when debugging problems you might wish for
help, and since "undefined behavior" can include catching the situation
and calling you a dummy, sometimes you can turn on debug modes/tools to
help you.

Martijn Lievaart

unread,
Dec 2, 2014, 8:55:16 AM12/2/14
to
Another typical example:

if (p && *p) ...

M4

Bo Persson

unread,
Dec 2, 2014, 11:36:06 AM12/2/14
to
So you obviously haven't used some obscure hardware, where
simultaneously reading and writing the same memory address would lock
the system up.

(can't remember its name right now).


BTW, v[i+1] = i+2; is the same number of characters, and a lot more obvious.



Bo Persson

Bo Persson

unread,
Dec 2, 2014, 11:41:15 AM12/2/14
to
On 2014-12-02 02:09, JiiPee wrote:
> On 01/12/2014 20:54, Lynn McGuire wrote:
>> "A quick poll about order of evaluation…"
>> http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evaluation/
>>
>> "Consider this program fragment:"
>>
>> " std::vector<int> v = { 0, 0 };
>> int i = 0;
>> v[i++] = i++;
>> std::cout << v[0] << v[1] << endl; "
>>
>> "My question is not what it might print under today’s C++ rules. The
>> third line runs afoul of two different categories of undefined and
>> unspecified behavior."
>>
>> "Rather, my question is what you would like the result to be. Please
>> let me know."
>>
>> My answer is 02.
>>
>> Lynn
>
> I agree with my GCC compiler: 10
>
> Because it should go from left to right like we are reading it. So [i++]
> first, then after that i++.
>
> I am interested; why should it be something else than this? We read (and
> the compiler as well) the code from top to bottom and from left to right
> normally, isnt it?

Not really. :-)

The problem here is that the compiler isn't required to store the result
of the increment back into i until the end of the expression. If you
read (or write) i again before that, we don't know what will happen.


Bo Persson


JiiPee

unread,
Dec 2, 2014, 1:30:47 PM12/2/14
to
yes, I have used this method a lot as well .. it is handy, just must be
100% sure it works like that first :).

JiiPee

unread,
Dec 2, 2014, 1:34:37 PM12/2/14
to
On 02/12/2014 10:42, Martijn Lievaart wrote:
> On Tue, 02 Dec 2014 01:25:17 +0000, JiiPee wrote:
>
>> On 02/12/2014 01:09, JiiPee wrote:
>>> On 01/12/2014 20:54, Lynn McGuire wrote:
>>> its the same with if-statement, isnt it?
>>> like:
>>> if( x > 5 || y < 10 )
>>> ...
> No. || is defined to evaluate from left to right *and* short circuit.

I did not mean that. Also I said later: "here if x > 5 will be checked
first and if its true y < 10 is not executed at all, isnt it? so its
from left to right there as well. " So exactly what you just said....So
it was not that I did not know this.

I meant, that because if-statement works from left to right like that,
then the original v[] -thing should also work from left to right imo.

>
>>> here if x > 5 will be checked first and if its true y < 10 is not
>>> executed at all, isnt it? so its from left to right there as well.
>> ok, but then Visual C++ 2008 gives: 00. So obviously if compilers give
>> different results this should be illegal (meaning there is no standard
>> rule for it).
> It is illegal. It's called undefined behaviour.
>

It is defined in the standard as undefined behaviour? Just asking...


red floyd

unread,
Dec 2, 2014, 1:34:38 PM12/2/14
to
Other than the lack of code context (and Herb explicitly said it was a
fragment), how is it ill-formed? It's semantically incorrect, but
where is the ill-formedness to be found?

JiiPee

unread,
Dec 2, 2014, 1:39:51 PM12/2/14
to
yes I agree here. and b first because it is on the left hand side. But
here a not first because multiplication should be done first according
to maths, so its natural. But with arrays I do not see natural that the
right hand side should be evaluated first. Or what is the logic there?

JiiPee

unread,
Dec 2, 2014, 1:42:58 PM12/2/14
to
and the whole thing is an expression? ok, i was not sure about this. If
this is true then its true there should be another rule for i++ how it
works in this kind of situation. I mean if i++ is done twice before its
required to change.

>
>

red floyd

unread,
Dec 2, 2014, 1:43:56 PM12/2/14
to
Yes.

Don't have the 2011 or 2014 standards, but ISO/IEC 14882:2003
specifically states 5/4 [expr]:

Except where noted, the order of evaluation of operands of individual
operators and subexpressions of individual expressions, and the order in
which side effects take place, is unspecified (53). *Between the
previous and next sequence point, a scalar object shall have its stored
value modified at most once by the evaulation of an expression*.
Furthermore, the prior value shall be accessed only to determine the
value to be stored. The requirements of this paragraph shall be met for
each allowable ordering of the subexpressions of a full expression;
*otherwise the behavior is undefined*.

(emphases mine)



JiiPee

unread,
Dec 2, 2014, 1:45:41 PM12/2/14
to
Whatever it is, in my opinion this kind of confusing code is not a good
practice. better to create temporary variables and use them instead -- I
mean one of them should be set using a temp variable before this and
then used.

red floyd

unread,
Dec 2, 2014, 1:45:43 PM12/2/14
to
On 12/2/2014 10:43 AM, red floyd wrote:

>>>>> here if x > 5 will be checked first and if its true y < 10 is not
>>>>> executed at all, isnt it? so its from left to right there as well.
>>>> ok, but then Visual C++ 2008 gives: 00. So obviously if compilers give
>>>> different results this should be illegal (meaning there is no standard
>>>> rule for it).
>>> It is illegal. It's called undefined behaviour.
>>>
>>
>> It is defined in the standard as undefined behaviour? Just asking...
>>
> Yes.
>
> Don't have the 2011 or 2014 standards, but ISO/IEC 14882:2003
> specifically states 5/4 [expr]:
>
> Except where noted, the order of evaluation of operands of individual
> operators and subexpressions of individual expressions, and the order in
> which side effects take place, is unspecified (53). *Between the
> previous and next sequence point, a scalar object shall have its stored
> value modified at most once by the evaulation of an expression*.
> Furthermore, the prior value shall be accessed only to determine the
> value to be stored. The requirements of this paragraph shall be met for
> each allowable ordering of the subexpressions of a full expression;
> *otherwise the behavior is undefined*.
>
> (emphases mine)

Further, the remainder of that paragraph explicitly gives

i = v[i++];

as an example of UB.



red floyd

unread,
Dec 2, 2014, 1:46:43 PM12/2/14
to
On 12/2/2014 10:42 AM, JiiPee wrote:

> and the whole thing is an expression? ok, i was not sure about this. If
> this is true then its true there should be another rule for i++ how it
> works in this kind of situation. I mean if i++ is done twice before its
> required to change.
>

It's explicitly UB. See the quote from the (2003) standard that I
posted upthread.



Chris Vine

unread,
Dec 2, 2014, 2:05:00 PM12/2/14
to
On Tue, 02 Dec 2014 08:49:30 -0500
Richard Damon <Ric...@Damon-Family.org> wrote:
> There is an argument that a = b should evaluate b first, and then
> process a to do the assignment. Also, given a + b * c some would say
> that the natural order to evaluate things would be b, c, b*c, a,
> a+b*c. These sort of expectations make a strict left to right become
> "surprising" at times. Yes, some languages will attempt to reduce
> undefined behavior by defining an order, inevitably this will
> sometimes be surprising to users, and will often force the compiler
> to generate less efficient code. One of the guiding principles of C
> that C++ inherited was that efficiency was important, and the
> compiler was allowed to assume that the programmer isn't trying hard
> to cause problems (if the programmer isn't following the rules, it is
> ok to bite them). This causes C and C++ to have perhaps more
> undefined behavior conditions than other languages, but also explains
> their popularity, programmers LIKE languages that trust them and
> generate faster results because of that. (Sometime when debugging
> problems you might wish for help, and since "undefined behavior" can
> include catching the situation and calling you a dummy, sometimes you
> can turn on debug modes/tools to help you.

I agree with that for the most part, but what annoys me most about C++
with regard to this, and I think offers little or no optimization
opportunities in return, is not unordered evaluation as such, but the
permit to partially evaluate. Take a function:

void func(std::unique_ptr<T1> a, std::unique_ptr<T2> b);

In the C++11 world (which does not have make_unique) the following code
represents a possible memory leak in the presence of an exception:

func(std::unique_ptr<T1>{new T1{val1}},
std::unique_ptr<T2>{new T2{val2}});

The problem with this is not that the order of evaluation of the
arguments is unspecified, but because the compiler is entitled to
partially evaluate by first evaluating the 'new T1{val1}' expression and
then evaluating the new 'T2{val2}' expression (or vice versa) before
constructing either unique_ptr object. This is OK with C but vastly
annoying with a language with exceptions such as C++. For exception
safety you have to do explicit sequencing yourself by hand, viz:

auto t1 = std::unique_ptr<T1>{new T1{val1};
auto t2 = std::unique_ptr<T2>{new T2{val2};
func(std::move(t1),
std::move(t2));

which is a pain in the butt.

I would really like to see that dealt with. The std::unique_ptr issue
is mostly solved by C++14's make_unique, but there are many other cases
where you might want to use handles where similar issues arise.

Chris

Chris Vine

unread,
Dec 2, 2014, 2:21:51 PM12/2/14
to
On Tue, 02 Dec 2014 10:43:45 -0800
red floyd <no....@its.invalid> wrote:
> Don't have the 2011 or 2014 standards, but ISO/IEC 14882:2003
> specifically states 5/4 [expr]:
>
> Except where noted, the order of evaluation of operands of individual
> operators and subexpressions of individual expressions, and the order
> in which side effects take place, is unspecified (53). *Between the
> previous and next sequence point, a scalar object shall have its
> stored value modified at most once by the evaulation of an
> expression*. Furthermore, the prior value shall be accessed only to
> determine the value to be stored. The requirements of this paragraph
> shall be met for each allowable ordering of the subexpressions of a
> full expression; *otherwise the behavior is undefined*.
>
> (emphases mine)

C++11, §1.9/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.

[ Example:
...
i = v[i++]; // the behavior is undefined "

C++14 is similar, although it imports an additional forward reference
to concurrency issues (§1.9/15 is concerned with the sequencing in a
single thread only).

Chris

JiiPee

unread,
Dec 2, 2014, 2:51:54 PM12/2/14
to
ok I got it. Its clear then...

Marcel Mueller

unread,
Dec 2, 2014, 4:14:23 PM12/2/14
to
On 01.12.14 21.54, Lynn McGuire wrote:
> std::vector<int> v = { 0, 0 };
> int i = 0;
> v[i++] = i++;
> std::cout << v[0] << v[1] << endl; "
>
> "My question is not what it might print under today’s C++ rules. The
> third line runs afoul of two different categories of undefined and
> unspecified behavior."
>
> "Rather, my question is what you would like the result to be. Please let
> me know."

Let's write it explicitly (I include the this pointers):

operator=(operator[](&v, i++), i++);

Each operator function call is a sequence point, but the two arguments
of operator= have no defined ordering. So the evaluation sequence of the
access to i is still undefined.

=> definitely undefined behavior.


Marcel

Paavo Helde

unread,
Dec 2, 2014, 4:46:20 PM12/2/14
to
Lynn McGuire <l...@winsim.com> wrote in news:m5ikhe$s8o$1...@dont-email.me:

> "A quick poll about order of evaluation…"
> http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evalua
> tion/
>
> "Consider this program fragment:"
>
> " std::vector<int> v = { 0, 0 };
> int i = 0;
> v[i++] = i++;
> std::cout << v[0] << v[1] << endl; "
>
> "My question is not what it might print under today’s C++ rules. The
> third line runs afoul of two different categories of undefined and
> unspecified behavior."
>
> "Rather, my question is what you would like the result to be. Please
> let me know."

I would like this to be a diagnosed error. A coworker of mine wrote some
similar code, but later he himself was not able to say which output he
expected. Neither can I with the above example, "10" seems as valid answer
as "02".

Cheers
Paavo

Öö Tiib

unread,
Dec 2, 2014, 9:22:07 PM12/2/14
to
The question was what I would like the third line to be. My answer is
that I would like it to be called badly formed by compiler. How exactly
to form the language rules ... I can't tell.

The reason is that there are no prevailing preference among developers
about evaluation order in that line. Regardless if standard did specify
{0,0}, {0,1} or {1,0} as its outcome the 60-70% of developers would find
it unintuitive. On the other hand lot of them do not even realize what
unreadable gibberish it is. So the only clean way out of the situation
is to call it officially illegal.

red floyd

unread,
Dec 3, 2014, 11:58:27 AM12/3/14
to
On 12/2/2014 6:21 PM, Öö Tiib wrote:
> On Tuesday, 2 December 2014 20:34:38 UTC+2, red floyd wrote:
>> On 12/1/2014 4:25 PM, Öö Tiib wrote:
>>> My answer is Other: the code is ill-formed and diagnostic is required.
>>>
>> Other than the lack of code context (and Herb explicitly said it was a
>> fragment), how is it ill-formed? It's semantically incorrect, but
>> where is the ill-formedness to be found?
>
> The question was what I would like the third line to be. My answer is
> that I would like it to be called badly formed by compiler. How exactly
> to form the language rules ... I can't tell.

>
Ah. Got ya.


0 new messages