Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this code well-defined?

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.

like image 327
Nawaz Avatar asked Jan 17 '11 03:01

Nawaz


3 Answers

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, and
  • Y is evaluated before the call to bar.

But an order such as this would still be possible:

  1. evaluate X
  2. evalute Y
  3. (sequence point separating X from foo call)
  4. call foo
  5. (sequence point separating Y from bar call)
  6. 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.

like image 139
jalf Avatar answered Oct 26 '22 09:10

jalf


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.

like image 26
Johannes Schaub - litb Avatar answered Oct 26 '22 07:10

Johannes Schaub - litb


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.

like image 25
David Gelhar Avatar answered Oct 26 '22 09:10

David Gelhar