我已经调试了一段时间的程序,最终发现错误是由于引用没有像我想象的那样更新。
这是一个显示我遇到的问题的示例:
#include <iostream>
using namespace std;
struct Test {
Test& set(int& i){ i = 10; return *this; }
Test& print(const int& i){ cout << i << endl; return *this; }
};
int main(void){
int i = 0;
Test t;
t.set(i).print(i + 5);
return 0;
}
我原本以为print()方法输出15,但输出5。
编辑:10天后我才意识到,用clang输出15!这是GCC中的错误吗?
答案 0 :(得分:10)
让我尝试解释一下C ++ 11标准。在§1.9/ 15中它说:
除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的。 [...]如果标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值的计算值未被排序,则行为未定义。
当然int
是标量类型,t.set(i).print(i + 5);
包含i
set()
和{1}}的副作用,因此如果没有注明否则,行为确实是未定义的。阅读§5.2.5(“类成员访问”),我找不到关于i + 5
运算符的序列的任何注释。 [但请参阅下面的编辑!]
请注意,当然保证.
在set()
之前执行,因为后者接收前者的返回值作为(隐式print()
)参数。
这里的罪魁祸首是this
的参数的值计算是 unsequenced 相对于print
的调用而言是不确定的。
编辑:在你的(@ Xeno)评论中读到答案后,我重读了标准中的段落,事实上它后来说:
调用函数(包括其他函数调用)中的每个评估(在执行被调用函数体之前或之后没有特别排序)在被调用函数的执行方面是不确定的顺序。
因为不确定序列不是未序列(“执行未经测序的评估可以重叠”,§1.9/ 13),这确实不是未定义的行为,而是“只是”未指明的行为,意味着15和5都是正确的输出。
因此,当set
表示“之前排序”且<
表示“不确定排序”时,我们有:
~
答案 1 :(得分:2)
在C ++中无法保证评估单个表达式中的函数参数的顺序,即使这些函数是链式方法调用也是如此。你在这里调用未定义的行为,这就是你得到的。
。 operator 确实意味着排序,但只有在表达式之前。必须在访问成员之前对其进行全面评估。这并不意味着子表达式的评估在此之前暂停。
另外,不要在int
之后传递const int&
,这不可能比直接传递int
更快(除非出于某种奇怪的原因int
没有不适合处理器字,参考确实如此。
答案 2 :(得分:0)
[评论太长了:]
如果添加
Test& add(int& i, const int toadd)
{
i += toadd;
return *this;
}
此次电话
t.set(i).add(i, 5).print(i);
返回
15
由此我得出结论,罪魁祸首是i + 5
作为打印的参数。