链式静态函数调用之间的参数评估顺序

Posted

技术标签:

【中文标题】链式静态函数调用之间的参数评估顺序【英文标题】:Argument evaluation order between chained static function calls 【发布时间】:2012-01-17 19:20:42 【问题描述】:

我很好奇为什么链式静态函数和成员函数之间的参数评估顺序存在差异。从this question 的答案中,我可以看到在这种链式函数调用之间的参数评估顺序是未指定的。以下面的sn-p为例:

#include <iostream>
class test 
public:
    static test& chain_s(test& t, int i) 
        std::cout << i << " ";
        return t;
    

    test& chain(test& t, int i) 
        std::cout << i << " ";
        return *this;
    
;

int main(int, char**) 
    int x = 2;
    test t;
    t.chain(t,++x).chain(t,++x).chain(t,++x);
    x = 2; std::cout << std::endl;
    t.chain_s(t,++x).chain_s(t,++x).chain_s(t,++x);

    return 0;

对于 GCC 4.6.2 和 CL 15.00.30729.01 (MSVC 9),结果输出适合我

5 5 5
3 4 5

但是,我想知道规范中是否有任何原因,或者是否知道为什么静态函数从左到右(使用它们的参数)进行评估,而对于非静态函数,所有参数首先(从我在其他测试中看到的从右到左)。

我之所以问这个问题是因为我在尝试在 C 中获得类似行为(使用结构和函数指针)并失败时首先注意到这种行为差异。我强烈怀疑这是在 GCC 和 MSVC 中针对成员函数实现的一些优化,但我希望这里有人能对此有所了解。

编辑: 我忘了提到一个让我觉得奇怪的关键信息:GCC 只会警告链式非静态函数的未指定行为,而不是静态函数:

a.cpp: In function 'int main(int, char**)':
a.cpp:18:45: warning: operation on 'x' may be undefined [-Wsequence-point]

GCC 没有义务提供此类警告,因此它可能会错过第二个表达式,但这让我相信正在发生一些有趣的事情。

【问题讨论】:

在你编写的任何代码中,这样的事情肯定应该是无关紧要的...... 是的,但我想知道为什么有些代码是错误的,但在类似情况下却不会产生警告。 【参考方案1】:

没有理由。就像你说的,语言没有指定顺序。

使用从右到左顺序的一个原因是具有可变数量参数的函数,例如printf,将始终将第一个参数放在顶部。否则没关系。

【讨论】:

我忘了问为什么编译器输出消息不同,有什么想法吗?这也是导致整个问题的原因。 即使使用printf,编译器也可以按照它喜欢的任何顺序评估参数,甚至交错评估。在基于堆栈的机器上,第一个参数可能是最后推送的,但编译器也很有可能只在堆栈上保留必要的空间,并使用 sp 相对寻址将每个参数存储在它所属的位置。并按照它认为方便的任何顺序进行。【参考方案2】:

您的代码具有未定义的行为,但我想您知道这一点。还, 您可以根据优化标志轻松看到差异。但 在这种情况下,一个可能的原因是非静态函数需要 三个参数,包括上一次调用的结果,其中 静态函数只需要两个,而前面的结果 调用被忽略。

【讨论】:

以上是关于链式静态函数调用之间的参数评估顺序的主要内容,如果未能解决你的问题,请参考以下文章

静态构造函数, 静态成员初始化/调用顺序

静态代码块非静态代码块构造函数之间的执行顺序

c#如何创建类 调用

在C中调用函数之前的参数评估顺序[重复]

java中静态方法,静态变量,静态初始化器,构造函数,属性初始化都是啥时候调用的? 它们的先后顺序。

用反射评估模块的静态构造函数