我显然必须遗漏一些非常基本的东西。
我有一个简单的类,我执行“就地”操作,然后返回对this
的引用(这允许我链接不同的操作)。当我打印到std::ostream
时,我看到一个意外的输出。以下代码将帮助我解释。
#include<iostream>
struct Container {
Container(int x, int y, int z)
: m_data{x, y, z} { }
int operator()(size_t index) const {
return m_data[index];
}
Container& shift() {
int tmp = m_data[0];
m_data[0] = m_data[1];
m_data[1] = m_data[2];
m_data[2] = tmp;
return *this;
}
int m_data[3];
};
std::ostream& operator<<(std::ostream& os, const Container& c) {
os<<"["<<c(0)<<", "<<c(1)<<", "<<c(2)<<"]";
return os;
}
int main() {
std::cout<<std::endl<<"Behaviour Line-By-Line (works as expected)"<<std::endl;
Container d(1, 2, 3);
std::cout<<"Container as built: "<<d<<std::endl;
d.shift();
std::cout<<"Container after first shift: "<<d<<std::endl;
d.shift();
std::cout<<"Container after second shift: "<<d<<std::endl;
std::cout<<std::endl<<"Behaviour On The Same Line (not as expected)"<<std::endl;
Container c(1, 2, 3);
std::cout<<"Container as built: "<<c<<std::endl
<<"Container after first shift: "<<c.shift()<<std::endl
<<"Container after second shift: "<<c.shift()<<std::endl;
return 0;
}
使用GCC 4.8.1编译(OS X 10.7.4)并运行:
$ g++ example.cpp -std=c++11 -Wall -Wextra
$ ./a.out
Behaviour Line-By-Line (works as expected)
Container as built: [1, 2, 3]
Container after first shift: [2, 3, 1]
Container after second shift: [3, 1, 2]
Behaviour On The Same Line (not as expected)
Container as built: [3, 1, 2]
Container after first shift: [3, 1, 2]
Container after second shift: [3, 1, 2]
正如您所看到的,当我将修改操作放在与operator<<
相同的行上时,输出似乎是缓冲区(因为没有更好的单词)更改。
我的问题是:
为什么会发生这种情况,如何使“On The Same Line”行为与“Line-By-Line行为”相匹配。
谢谢!
根据@ KeithSmith的建议,我将Container::shift
修改为:
Container& shift() {
std::cout<<"shifting... "<<std::flush;
int tmp = m_data[0];
m_data[0] = m_data[1];
m_data[1] = m_data[2];
m_data[2] = tmp;
return *this;
}
得到了输出:
Behaviour Line-By-Line (works as expected)
Container as built: [1, 2, 3]
shifting... Container after first shift: [2, 3, 1]
shifting... Container after second shift: [3, 1, 2]
Behaviour On The Same Line (not as expected)
shifting... shifting... Container as built: [3, 1, 2]
Container after first shift: [3, 1, 2]
Container after second shift: [3, 1, 2]
如几个答案所述,未定义操作的顺序。在“不尽如人意”的情况下,转换发生在流式传输之前,但我想它可能以任何顺序发生。
我的外卖:inlining
有副作用的手术时非常非常小心!我想我应该知道这个!可悲的是我没有!
答案 0 :(得分:2)
未指定评估函数参数的顺序。如果你的表达有副作用,并且你依赖于他们的顺序,你需要确保按顺序执行。即,陈述
std::cout<<"Container as built: "<<c<<std::endl
<<"Container after first shift: "<<c.shift()<<std::endl
<<"Container after second shift: "<<c.shift()<<std::endl;
可以作为
处理c.shift()
c.shift()
它也可以以不同的顺序评估表达式。只是移位运算符的函数调用是有序的,显然,它取决于子表达式,需要首先评估子表达式。
答案 1 :(得分:1)
第二个cout大致翻译为operator <<(operator <<(operator <<(cout, c), c.shift(), c.shift())
,由于参数评估顺序未指定,所有的转换都可以在开头发生。
第一个;
中的cout
引入了序列点,确保了评估顺序。参见例如这个:Undefined behavior and sequence points了解有关序列点的更多信息。