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

4

Consider the following code:

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

奇怪的是,编译器生成的代码会在评估intResult0之前评估intResult1(使用最新的VC和gcc进行测试)。 为什么编译器会这样做?这样做会增加各个值之间的评估和使用时间(不必要地),即先获取42,但最后将其推入向量中。 C++标准是否规定了这一点?


哈哈,昨晚我也遇到了operator+=的问题。让我感到困惑的是,阅读代码时,你会认为intResult1必须在第二个被调用,因为它使用intResult0返回的值作为其第一个参数。 - Dolphin
4个回答

14
在两个序列点之间,子表达式的求值顺序是未定义的。
上述代码是以下代码的语法糖:
v.operator<<(intResult0()).operator<<(intResult1());

编译器唯一的限制是在调用方法前必须评估所有参数并遵守优先级规则。但只要遵循这些规则,每个实现都可以选择细节,因此编译器之间的顺序可能会改变。
在本例中:
- 在调用intResult2()之前调用intResult1()是完全合法的。 - 但必须在调用operator<<(左)之前调用intResult0()。 - 必须在调用operator<<(右)之前调用intResult1()。 - 必须先调用operator<<(左),再调用operator<<(右)。
更多信息请参见此处: C++程序员应该知道的所有常见未定义行为是什么?C++程序员应该知道的所有常见未定义行为是什么?

13
根据 Stroustrup 6.2.2 节所述:

表达式中子表达式的计算顺序是未定义的。


10

这与优先级无关。

在最后一条语句中没有序列点,因此编译器可以自由地按任意顺序计算子表达式,只要在组合子表达式时使用优先级即可。

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

例如,在以下表达式中:

a() * b() + c()
在某个时刻,编译器需要先计算 (a() * b()) 的结果再加上 c() 的结果,但并没有规定每个函数调用的顺序。编译器可以很容易地决定先调用 c(),将结果推入堆栈,然后做必要的操作来计算表达式 (a()*b())的值(在这种情况下,它可能会决定先计算 b())。
优先级扮演的唯一角色是编译器不允许按照以下方式计算该表达式的值:
a() * (b() + c())

2

C++标准,5:4

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值顺序以及副作用发生的顺序是未指定的


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接