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

时间:2009-06-15 15:44:12

标签: 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();

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

4 个答案:

答案 0 :(得分:14)

两个序列点之间的子表达式的评估顺序是未定义的。

上面的代码是语法糖:

v.operator<<(intResult0()).operator<<(intResult1());

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

在这个例子中:

  • 因此在intResult2()之前调用intResult1()是完全合法的。
  • 但必须在调用operator&lt;&lt;()之前调用intResult0()(左)
  • 必须在调用运算符&lt;&lt;()(右)之前调用
  • 和intResult1()
  • 必须在operator&lt;&lt;()(右)之前调用
  • 和operator&lt;&lt;()(左)

有关详细信息,请参阅此处:
What are all the common undefined behaviours that a C++ programmer should know about?

What are all the common undefined behaviours that a C++ programmer should know about?

答案 1 :(得分:12)

根据Stroustrup第6.2.2节:

  

评估的顺序   表达式中的子表达式是   未定义。

答案 2 :(得分:10)

这与优先权无关。

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

请注意,优先级定义评估的整体顺序 - 它只定义了如何组合具有多个运算符的表达式的操作数。

例如,在以下表达式中:

a() * b() + c()

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

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

b()

答案 3 :(得分:2)

C ++标准,5:4

  

除非另有说明,否则顺序为   评估个人的操作   运算符和子表达式   个人表达和秩序   发生副作用的是   未指定的