Topic: order of operation (not operator precedence)
Author: Kaleb Pederson <kibab@icehouse.net>
Date: Sat, 28 Apr 2001 23:35:10 GMT Raw View
Does the C++ standard specifically say that the following must be
initialized from right to left or is this left to the compiler designer?
Assuming, of course, that actual_value_here and the corresponding variables
are of the correct type?
test->val1->val2 = test->val1 = test = actual_value_here;
Thanks for the info.
--Kaleb Pederson
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Jack Klein <jackklein@spamcop.net>
Date: Sun, 29 Apr 2001 13:15:22 GMT Raw View
On Sat, 28 Apr 2001 23:35:10 GMT, Kaleb Pederson <kibab@icehouse.net>
wrote in comp.std.c++:
> Does the C++ standard specifically say that the following must be
> initialized from right to left or is this left to the compiler designer?
> Assuming, of course, that actual_value_here and the corresponding variables
> are of the correct type?
>
> test->val1->val2 = test->val1 = test = actual_value_here;
>
> Thanks for the info.
>
> --Kaleb Pederson
The language requires that this is equivalent to:
test->val1->val2 = (test->val1 = (test = actual_value_here));
That is, the right-most assignment statement is evaluated, and the
value of that assignment (the value of "actual_value_here" converted
to the type of test, if test is of a different type) is then available
for evaluating the value to be assigned to test->val1, possibly
converted, and that value is available for evaluating the value to be
assigned to test->val1->val2.
That does not mean that the assignments to the destination objects are
actually made in that order. They may be made in any order at all,
the only requirement is that they are all made by the sequence point
(the ;) that ends the statement.
In fact, as written your statement has undefined behavior. Both test
and test->val1 are modified, and their values are used for a purpose
other than computing the new value without an intervening sequence
point. This is true even if these are objects with overloaded
assignment operators.
--
Jack Klein
Home: http://JK-Technology.Com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Sun, 29 Apr 2001 13:23:28 GMT Raw View
Kaleb Pederson wrote:
>
> Does the C++ standard specifically say that the following must be
> initialized from right to left or is this left to the compiler designer?
> Assuming, of course, that actual_value_here and the corresponding variables
> are of the correct type?
>
> test->val1->val2 = test->val1 = test = actual_value_here;
Let's first consider a simpler case:
a = b = c = d;
The grammar defined by the standard forces that expression to be
evaluated the same way as
a = (b = (c = d));
So in that sense, the answer to your question is "yes". However, you've
got a much more complicated expression, with a total of 6 operators.
Using parenthesis to demonstrate how the statement must be parsed:
> ((test->val1)->val2) = ( (test->val1) = (test = actual_value_here));
1 2 3 4 5 6
The compiler is free to choose which order it wants to evalue these
expressions, subject to certain restrictions. Let "E1 < E2" indicate
that expression based on operator 1 must be evaluated before the one
based on operator 2. Then the following restrictions apply:
E1 < E2 < E3
E4 < E5
E6 < E5 < E3
There are no other restrictions on the order of evaluation. One
possibility is:
E1, E2, E4, E6, E5, E3
Another, possibility, however, is
E6, E4, E5, E1, E2, E3
However, in itself that doesn't mean anything important. The tricky part
is the side effects. When you write 'a=b', I'm sure that you tend to
think of the main purpose of that expression is to set the value of 'a'
to equal the value of 'b'. However, that's not the way the standard
describes it. From the point of view of the standard, that's just a
side-effect. The main effect of an assignment operator is to return a
value.
Why does this matter? Because the standard says that implementors are
free to rearrange the order of side-effects in certain circumstances.
There are things called sequence points, which are associated with
certain operators. The standard says that if two expressions are
seperated by a sequence point, then all the side-effects of the first
expression must be complete before any of the side effects of the second
expression can start. I won't bother boring you with a detailed
explanation of sequence points. I'm just going to tell you that the only
sequence point associated with that statement is the one at the very end
of the statement. It also has three operators with side-effects; the
three assignement operators. Lets call those side effects V3, V5 and V6.
Then the only constraints on those side effects are:
E3 < V3
E5 < V5
E6 < V6
In particular, you could have the following order:
E6, E4, E5, E1, E2, E3, V3, V5, V6
I suspect that you were expecting the standard would say that it had to
be the following order:
E1, E2, E4, E6, V6, E5, V5, E3, V6
Note that this is an important difference; the value of 'test' that is
used when evaluating "test->" could be exactly the same as the initial
value of 'test'; that value might not change until the very end of the
statement. I'm virtually certain that's not what you want.
It gets worse. Everything I've said so far would be equally true if
'test' were three different variables in each of the three places you
use it. However, there's an additional rule:
"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. Furthermore,
the prior value
shall be read only to determine the value to be stored."
Your statement modifies the stored value of 'test', and reads that value
twice for purposes other than determining the value to be stored. Since
there is not a single intervening sequence point, your statement
violates that rule. Violating a "shall" that isn't part of a constraint
allows undefined behavior; that's pretty much the most severely negative
thing the standard can say about a piece of code.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Kaleb Pederson <kibab@icehouse.net>
Date: Sun, 29 Apr 2001 18:34:14 GMT Raw View
James (and Jack),
Thank you for your responses. Your response wasn't light reading but makes
perfect sense (after a little bit of work to understand it). Thanks for
teaching me, and others who read this newsgroup.
--Kaleb
James Kuyper Jr. wrote:
....
> Let's first consider a simpler case:
>
> a = b = c = d;
>
> The grammar defined by the standard forces that expression to be
> evaluated the same way as
>
> a = (b = (c = d));
>
> So in that sense, the answer to your question is "yes". However, you've
> got a much more complicated expression, with a total of 6 operators.
> Using parenthesis to demonstrate how the statement must be parsed:
>
>> ((test->val1)->val2) = ( (test->val1) = (test = actual_value_here));
> 1 2 3 4 5 6
> ...
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]