This code is taken from a discussion going on here.
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
Is this code well-defined? Is ++k
in Fun() evaluated before k
in Sun()?
What if k
is user-defined type, not built-in type? And in what ways the above function calls order is different from this:
eat(++k);drink(10);sleep(k);
As far as I know, in both situations, there exists a sequence point after each function call. If so, then why can't the first case is also well-defined like the second one?
Section 1.9.17 of the C++ ISO standard says this about sequence points and function evaluation:
When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function.
I think if you read exactly what that standard quote says, the first case won't be well-defined:
When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body
What this tells us is not that "the only thing that can happen after the arguments for a function have been evaluated is the actual function call", but simply that there is a sequence point at some point after the evaluation of arguments finishes, and before the function call.
But if you imagine a case like this:
foo(X).bar(Y)
the only guarantee this gives us is that:
X
is evaluated before the call to foo
, andY
is evaluated before the call to bar
.But an order such as this would still be possible:
X
Y
X
from foo
call)foo
Y
from bar
call)bar
and of course, we could also swap around the first two items, evaluating Y
before X
. Why not? The standard only requires that the arguments for a function are fully evaluated before the first statement of the function body, and the above sequences satisfy that requirement.
That's my interpretation, at least. It doesn't seem to say that nothing else may occur between argument evaluation and function body -- just that those two are separated by a sequence point.
This depends on how Sun
is defined. The following is well-defined
struct A {
A &Fun(int);
A &Gun(int);
A &Sun(int&);
A &Tun();
};
void g() {
A someInstance;
int k = 0;
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
}
If you change the parameter type of Sun
to int
, it becomes undefined. Let's draw a tree of the version taking an int
.
<eval body of Fun>
|
% // pre-call sequence point
|
{ S(increment, k) } <- E(++x)
|
E(Fun(++k).Gun(10))
|
.------+-----. .-- V(k)--%--<eval body of Sun>
/ \ /
E(Fun(++k).Gun(10).Sun(k))
|
.---------+---------.
/ \
E(Fun(++k).Gun(10).Sun(k).Tun())
|
% // full-expression sequence point
As can be seen, we have a read of k
(designated by V(k)
) and a side-effect on k
(at the very top) that are not separated by a sequence point: In this expression, relative to each other sub-expression, there is no sequence point at all. The very bottom %
signifies the full-expression sequence point.
This is undefined behavior, because the value of k is being both modified and read in the same expression, without an intervening sequence point. See the excellent long answer to this question.
The quote from 1.9.17 tells you that all function arguments are evaluated before the body of the function is called, but doesn't say anything about the relative order of evaluation of arguments to different function calls within the same expression -- no guarantee that "++k Fun() is evaluated before k in Sun()".
eat(++k);drink(10);sleep(k);
is different because the ;
is a sequence point, so the order of evaluation is well-defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With