Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this code well defined?

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).

like image 295
Zach Saw Avatar asked Jul 14 '11 01:07

Zach Saw


2 Answers

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.)

like image 110
Nemo Avatar answered Oct 20 '22 03:10

Nemo


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().

like image 36
Cubbi Avatar answered Oct 20 '22 03:10

Cubbi