8

我怀疑根据 C++ 标准(假设 C++0x),以下函数链接会导致未指定的序列。只需要确认,如果有人可以提供解释,我将不胜感激。

#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());
}

*编辑:删除函数的 __fastcall 说明符(不需要/与问题相关)。

4

2 回答 2

8

未指定评估顺序。C++0x 规范草案的相关部分是 1.9,第 14 和 15 段:

14 与完整表达式关联的每个值计算和副作用在与要评估的下一个完整表达式关联的每个值计算和副作用之前排序。

15 除非另有说明,对单个运算符的操作数和单个表达式的子表达式的求值是无序的。

这里相关的完整表达式是:

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

所以它的子表达式的评估是无序的(除非在某处我错过了一个例外)。

我很确定早期的标准包括具有相同效果但就“序列点”而言的语言。

[编辑]

第 15 段还说:

调用函数时(无论该函数是否内联),与任何参数表达式或与指定被调用函数的后缀表达式相关的每个值计算和副作用都在执行主体中的每个表达式或语句之前进行排序称为函数。[注意:与不同参数表达式相关的值计算和副作用是无序的。-结束注释]

“指定被调用函数的后缀表达式”类似于foo().barin foo().bar()

这里的“注释”只是澄清了参数评估顺序不是“未指定顺序”默认值的例外。通过推断,评估顺序也与“指定被调用函数的后缀表达式”相关联;this或者,如果您愿意,参数表达式的求值顺序。(如果有例外,这将是指定它的自然位置。或者可能是第 5.2.2 节讨论函数调用。这两个部分都没有说明此示例的评估顺序,因此未指定。)

于 2011-07-14T01:24:42.530 回答
1

是的,函数参数的评估顺序是未指定的。

对我来说,Linux 上的 gcc 4.5.2 产生

bar3
bar2
bar1
TFoo
foobar1
foobar2

但是 linux 上的 clang++ 和 solaris 上的 gcc 3.4.6 会产生

bar1
TFoo
bar2
foobar1
bar3
foobar2

分析一个更简单的例子,TFoo(0).foobar1(TFoo::bar2());是一个TFoo::foobar1接受两个参数的调用:子表达式的结果TFoo(0)(作为隐藏参数this)和子表达式的结果Tfoo::bar2()。对我来说,gccbar2()先执行,然后是 TFoo 的构造函数,然后调用foobar1(),而例如 clang++,先执行 TFoo 的构造函数,然后bar2()再调用foobar1().

于 2011-07-14T01:23:57.490 回答