8

I suspect the following chaining of functions would result in unspecified sequence according to the C++ standards (assume C++0x). Just want a confirmation and if anyone could provide an explanation, I'd appreciate it.

#include <iostream>

struct TFoo 
{
    TFoo(int) 
    {
        std::cout<<"TFoo"<<std::endl;
    };
    TFoo foobar1(int) 
    {
        std::cout<<"foobar1"<<std::endl;
        return *this;
    };
    TFoo foobar2(int) 
    {
        std::cout<<"foobar2"<<std::endl;
        return *this;
    };
    static int bar1() 
    {
        std::cout<<"bar1"<<std::endl;
        return 0;
    };
    static int bar2() 
    {
        std::cout<<"bar2"<<std::endl;
        return 0;
    };
    static int bar3()
    {
        std::cout<<"bar3"<<std::endl;
        return 0;
    }
};

int main(int argc, char *argv[])
{
    // is the sequence well defined for bar1, bar2 and bar3?
    TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3());
}

* edit: removed __fastcall specifier for functions (not required/relevant to the question).

Zach Saw
  • 4,308
  • 3
  • 33
  • 49
  • 1
    I don't mean to come across as nitpicky, but technically speaking `__fastcall` isn't part of the C++ spec, so I don't think the spec has anything to say about this. Is there any reason you included it here? Or is the question specifically about the interaction of `__fastcall` with sequence points? – templatetypedef Jul 14 '11 at 01:06

2 Answers2

8

The evaluation order is not specified. The relevant section of the draft C++0x spec is 1.9, paragraphs 14 and 15:

14 Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

Here the relevant full-expression is:

TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3());

And so the evaluation of its subexpressions are unsequenced (unless there is an exception noted somewhere that I missed).

I am pretty sure earlier standards include language having the same effect but in terms of "sequence points".

[edit]

Paragraph 15 also says:

When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [Note: Value computations and side effects associated with different argument expressions are unsequenced.— end note]

A "postfix expression designating the called function" is something like the foo().bar in foo().bar().

The "note" here merely clarifies that argument evaluation order is not an exception to the "unspecified order" default. By inference, neither is the evaluation order associated with the "postfix expression designating the called function"; or if you prefer, the evaluation order of the expression for the this argument. (If there were an exception, this would be the natural place to specify it. Or possibly section 5.2.2 that talks about function calls. Neither section says anything about the evaluation order for this example, so it is unspecified.)

Community
  • 1
  • 1
Nemo
  • 70,042
  • 10
  • 116
  • 153
  • @Zach: Indeed. But if you plan to depend on evaluation order, you need to find a specific rule that sets the order. C/C++ has always been been loose about specifying this to give compilers and CPUs room to re-order things for performance. – Nemo Jul 14 '11 at 01:49
  • 1
    IMO important to mention is that `foobar1` is called before `foobar2`. So the order is constrained (because the result of `foobar1()` needs to be evaluated before it is used as an operand of `operator.`, 1.9p15), though mostly unspecified with respect to the other calls. – Johannes Schaub - litb Jul 14 '11 at 09:23
1

Yes, the order of evaluation of function arguments is unspecified.

For me, gcc 4.5.2 on linux produces

bar3
bar2
bar1
TFoo
foobar1
foobar2

but clang++ on linux and gcc 3.4.6 on solaris produce

bar1
TFoo
bar2
foobar1
bar3
foobar2

To analyze a simpler example, TFoo(0).foobar1(TFoo::bar2()); is a call to TFoo::foobar1 which takes two arguments: the result of the subexpression TFoo(0) (as the hidden argument this) and the result of the subexpression Tfoo::bar2(). For me, gcc executs bar2() first, then TFoo's constructor, and then calls foobar1(), while clang++ for example, executes TFoo's constructor first, then bar2() and then calls foobar1().

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • I know order of evaluation of function arguments is unspecified but in this case, it's really the order of eval of function argument (singular) for each chained function (which has its order specified) that is in question. Any relevant standards to say this is truly unspecified? – Zach Saw Jul 14 '11 at 01:31
  • @Zach Saw: Member functions take `this` as an implied argument too. – Cubbi Jul 14 '11 at 01:35
  • care to elaborate? does that mean that the order is unspecified too for the chained functions (foobar1 and foobar2)? – Zach Saw Jul 14 '11 at 01:38
  • @Zach Saw: no, `foobar2` will always be called after `foobar1` because the result of foobar1 is an argument of foobar2. A function call is initiated after all of its arguments have been calculated. – Cubbi Jul 14 '11 at 01:44
  • PS: this is informal, of course, the standardese for "the `this` argument" is "postfix subexpression" as posted by @Nemo. Keeping the answer for the sake of a real-life example. – Cubbi Jul 14 '11 at 02:03