C++移位运算符优先级怪异

new*_*gre 4 c++ operator-overloading

请考虑以下代码:

typedef vector<int> intVec;

intVec& operator<<(intVec& dst, const int i) {
    dst.push_back(i);
    return dst;
}
int intResult0() {
    return 23;
}
int intResult1() {
    return 42;
}

// main
intVec v;
v << intResult0() << intResult1();
Run Code Online (Sandbox Code Playgroud)

奇怪的是,编译器生成代码,该代码评估intResult1 BEFORE intResult0(使用最新的VC和gcc进行测试).为什么编译器会这样做?通过这样做,评估和使用各个值之间的时间(不必要地)增加(α),即首先获取42,但最后推送到矢量.C++标准是否规定了这一点?

Mar*_*ork 14

两个序列点之间的子表达式的评估顺序是不确定的.

上面的代码是语法糖:

v.operator<<(intResult0()).operator<<(intResult1());
Run Code Online (Sandbox Code Playgroud)

编译器唯一的约束是它必须在调用方法之前评估所有参数并遵守优先规则.但只要遵循这些规则,每个实现都允许选择细节,因此这个顺序可能会在编译器之间发生变化.

在这个例子中:

  • 因此在intResult2()之前调用intResult1()是完全合法的.
  • 但是在调用operator <<()之前必须调用intResult0()(左)
  • 必须在调用operator <<()之前调用intResult1()(右)
  • 和operator <<()(左)必须在operator <<()之前调用(右)

有关更多信息,请参阅此处:
C++程序员应该了解的所有常见的未定义行为是什么?

C++程序员应该知道的所有常见的未定义行为是什么?


Joh*_*don 12

根据Stroustrup第6.2.2节:

表达式中子表达式的评估顺序是不确定的.


Mic*_*urr 10

这与优先权无关.

在最后一个语句中没有序列点,因此只要在组合子表达式时使用优先级,编译器就可以按照它喜欢的顺序自由地计算子表达式.

需要注意的是优先级并没有规定评估的整体秩序-它只是定义了如何表达与几家运营商的操作数将被合并.

例如,在以下表达式中:

a() * b() + c()
Run Code Online (Sandbox Code Playgroud)

在某些时候,编译器需要(a() * b())在添加结果之前进行评估c(),但没有任何内容表明每个函数调用需要进行的顺序.编译器可以很容易地决定先调用c(),将结果推送到堆栈,然后做任何需要做的事情来评估(a() * b())表达式(在这种情况下,它可能决定先评估b()).

优先级所扮演的唯一角色是不允许编译器将表达式计算为:

a() * (b() + c())
Run Code Online (Sandbox Code Playgroud)