任何其他带有链式插入运算符的陷阱
Any other gotchas with chained insertion operators?
所以,我今天早些时候在搞砸C++,特别是链接插入运算符。我注意到一些对我来说似乎很奇怪的事情。
#include <iostream>
using namespace std;
size_t& foo(size_t& n) {
++n;
return n;
}
int main() {
size_t bar = 5;
cout << bar << " a " << foo(bar) << " b " << bar;
cin >> bar; //Ignore this, it's only here as an easy way to keep the window open
}
运行这个,而不是给5 a 6 b 6
实际上给了6 a 6 b 5
.显然,插入操作数是从右到左计算的,但随后从左到右打印,这可以解释为什么更新的值出现在函数调用之前,而原始值出现在函数调用之后。
当然,这可以通过简单地将cout foo(bar)
放在自己的行上来解决,但我跑题了。
链接插入运算符时,我是否应该注意其他类似奇怪的事情?另外,有谁知道插入运算符为什么要这样做?
您正在读取 bar 两次,并为函数调用引用它一次,这会递增它。该函数调用相对于读取是不确定的排序的,这些读取彼此不排序,因此允许任何排序,并且未指定您获得的顺序。
你没有完全没有未定义的行为(一切都会),但未指定的行为并没有更有趣。以下是四个有效输出:
5 a 6 b 5
5 a 6 b 6
6 a 6 b 5
6 a 6 b 6
"链接插入运算符"只是函数调用。C++该语句中有一些关于函数调用的简单规则:
- 单个语句中的函数调用不交错 - 一个在下一个语句开始之前完成。(即语句内没有多线程) 当一个函数调用的
- 返回值是另一个函数调用的参数时,第一个函数调用必须在第二个函数调用之前发生。
没有规则规定单个函数的多个参数必须在调用之前连续或立即计算 - 排序规则集非常短。
现在我们在链中看到的是,不同的operator<<
调用确实是链式的:第二个调用使用第一个调用的返回值。这就是为什么这些运算符都返回std::ostream&
.因此,您的输出以正确的顺序显示。
在打印其返回值之前,必须调用具有相同逻辑的foo(bar)
。但它可以在任何先前的时间调用。回想一下,没有规定必须同时计算单个函数的 2 个参数。第三个operator<<
调用的两个参数是 foo(bar)
的返回值和第二个operator<<
的返回值。因此,打印第三个值时,两者都必须发生。因此,a
之后打印的数字是 6。
如果您声明结果应5 a 6 b 6
,则假定第五个调用的第二个参数在第三个调用的第二个参数之后计算。根本没有这方面的规则。
- 重载嵌套结构/类的流插入运算符
- 屏幕插入运算符<<的运算符过载问题
- 类方法的C++插入运算符
- 在设计方面:重载vector类型的类成员的插入运算符
- 我在C++中遇到插入运算符错误
- 基类和派生类的虚拟插入运算符重载
- 如何在 std::map<const int、int> C++ 中重载插入运算符>>?
- 插入运算符的过载与使用二传手功能相比
- Visual Studio 15:重载"<<" C++中的插入运算符
- 使用 std::stringbuf 进行缓冲的效果,同时通过插入运算符'<<'执行写入
- 为什么当我使用额外的括号而不使用运算符重载时,插入运算符在 std::cout 中给出不同的结果?
- 编译器在遇到提取或插入运算符时处理信息(字符串、操纵器等)的顺序是什么?
- 插入运算符无法使用矢量,我不知道为什么
- 在C++中,我们如何将插入运算符和其他运算符链接在一起
- 插入运算符的非成员函数窗体
- C++序列化:使用write()重载插入运算符
- 子类中的重载流插入运算符 (<<)
- 重载提取和插入运算符 C++
- 流插入 (<<) 运算符过载?
- ostream 插入运算符与其非成员重载之间的关系