Search code examples
c++operator-precedencesequence-points

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.


Solution

  • 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(++k) 
                                 |     
                          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.