使用 std::cout 评估参数的顺序

Posted

技术标签:

【中文标题】使用 std::cout 评估参数的顺序【英文标题】:Order of evaluation of arguments using std::cout 【发布时间】:2011-10-10 20:49:22 【问题描述】:

大家好,我今天偶然发现了这段代码,我对到底发生了什么感到困惑,更具体的说是什么顺序:

代码:

#include <iostream>

bool foo(double & m)

    m = 1.0;
    return true;


int main()

    double test = 0.0;
    std::cout << "Value of test is : \t" << test << "\tReturn value of function is : " << foo(test) <<  "\tValue of test : " << test << std::endl;
    return 0;

输出是:

Value of test is :      1       Return value of function is : 1 Value of test : 0

看到这一点,我会假设在调用函数之前以某种方式打印了最正确的参数。所以这是从右到左的评价??在调试期间,虽然似乎在输出之前调用了该函数,这是我所期望的。我正在使用 Win7 和 MSVS 2010。感谢任何帮助!

【问题讨论】:

【参考方案1】:

表达式中元素的求值顺序是未指定的(除了一些非常特殊的情况,例如&amp;&amp;|| 运算符和三元运算符,它们引入了序列点);因此,不能保证 test 将在 foo(test) 之前或之后评估(修改它)。

如果您的代码依赖于特定的求值顺序,最简单的获取方法是将表达式拆分为几个单独的语句。

【讨论】:

您知道使用||&amp;&amp;&lt;&lt; 链中强制执行某个评估顺序的技巧吗? 不,但我知道使用; 并在换行符后重复流变量的技巧 ;-) 也许用 C++17 标准中引入的更改来更新这个答案会对将来遇到这个问题的人有所帮助:***.com/a/50361417/9385966【参考方案2】:

这个问题的答案在 C++17 中发生了变化。

重载运算符的求值现在以与内置运算符相同的方式排序 (C++17 [over.match.oper]/2)。

此外,&lt;&lt;&gt;&gt; 和下标运算符现在将左操作数 先排序 右操作数,函数调用的后缀表达式 先排序 em> 对参数的评估。

(其他二元运算符保留其先前的排序,例如+ 仍然未排序)。

所以问题中的代码现在必须输出Value of test is : 0 Return value of function is : 1 Value of test : 1。但是“不要这样做”的建议仍然是合理的,因为每个人都需要一些时间才能更新到 C++17。

【讨论】:

【参考方案3】:

未指定评估顺序。它不是从左到右、从右到左或其他任何东西。

不要这样做。

【讨论】:

【参考方案4】:

求值顺序不详,见http://en.wikipedia.org/wiki/Sequence_point

这与带有运算符+示例的示例相同:

考虑两个函数f()g()。在 C 和 C++ 中,+ 运算符与序列点无关,因此在表达式 f()+g() 中,f()g() 可能会首先执行。

【讨论】:

【参考方案5】:

c++ 参考很好地解释了为什么不应该这样做(导致 UB 或未定义的行为) https://en.cppreference.com/w/cpp/language/operator_incdec

#include <iostream>

int main()

    int n1 = 1;
    int n2 = ++n1;
    int n3 = ++ ++n1;
    int n4 = n1++;
//  int n5 = n1++ ++;   // error
//  int n6 = n1 + ++n1; // undefined behavior
    std::cout << "n1 = " << n1 << '\n'
              << "n2 = " << n2 << '\n'
              << "n3 = " << n3 << '\n'
              << "n4 = " << n4 << '\n';

备注

由于涉及的副作用,内置增量和减量 必须小心使用运算符以避免未定义的行为,因为 违反排序规则。


在与排序规则相关的部分中,您可以阅读以下内容:

未定义的行为:

1) 如果一个标量对象的副作用相对于同一标量对象的另一个副作用是未排序的,则行为未定义。

i = ++i + 2;       // undefined behavior until C++11
i = i++ + 2;       // undefined behavior until C++17
f(i = -2, i = -2); // undefined behavior until C++17
f(++i, ++i);       // undefined behavior until C++17, unspecified after C++17
i = ++i + i++;     // undefined behavior

2) 如果标量对象的副作用相对于使用相同标量对象的值的值计算而言是无序的,则行为未定义。

cout << i << i++; // undefined behavior until C++17
a[i] = i++;       // undefined behavior until C++17
n = ++i + i;      // undefined behavior 

【讨论】:

以上是关于使用 std::cout 评估参数的顺序的主要内容,如果未能解决你的问题,请参考以下文章

虚拟基类的创建顺序

虚拟基类的创建顺序

C ++ 11成员类初始化顺序[重复]

构造函数解析顺序的问题

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

执行中的线程顺序