为什么下面程序的输出是什么?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
产生
2+3 = 15
而不是预期的
2+3 = 5
这个问题已经经历了多个关闭/重新打开周期。
在结束投票之前,请考虑一下有关此问题的元讨论。
为什么下面程序的输出是什么?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
产生
2+3 = 15
而不是预期的
2+3 = 5
这个问题已经经历了多个关闭/重新打开周期。
在结束投票之前,请考虑一下有关此问题的元讨论。
Answers:
无论是有意还是无意,您都<<
在第一个输出行的末尾,可能是您想要的;
。所以你基本上有
cout << "2+3 = "; // this, of course, prints "2+3 = "
cout << cout; // this prints "1"
cout << 2 + 3; // this prints "5"
cout << endl; // this finishes the line
因此,问题归结为:为什么cout << cout;
打印"1"
?
事实证明,这可能是微妙的。std::cout
通过其基类std::basic_ios
,提供了某种类型转换运算符,该运算符打算在布尔上下文中使用,例如
while (cout) { PrintSomething(cout); }
这是一个非常糟糕的示例,因为很难使输出失败-但std::basic_ios
实际上它是输入流和输出流的基类,对于输入而言,它更有意义:
int value;
while (cin >> value) { DoSomethingWith(value); }
(在流的末尾或当流字符未形成有效整数时退出循环)。
现在,在标准的C ++ 03和C ++ 11版本之间,此转换运算符的确切定义已更改。在旧版本中,它operator void*() const;
(通常实现为return fail() ? NULL : this;
),而在较新版本中,它explicit operator bool() const;
(通常实现为return !fail();
)。两种声明都可以在布尔上下文中正常工作,但是当(错误)在此类上下文之外使用时,它们的行为会有所不同。
特别是,在C ++ 03规则下,cout << cout
将被解释为cout << cout.operator void*()
并打印一些地址。在C ++ 11规则下,cout << cout
根本不应该编译,因为已声明运算符explicit
,因此不能参与隐式转换。实际上,这是进行更改的主要动机-防止不必要的代码编译。符合这两个标准的编译器都不会生成可打印的程序"1"
。
显然,某些C ++实现允许混合并匹配编译器和库,从而产生不一致的结果(引用@StephanLechner:“我在xcode中发现了一个设置,该设置产生1,而另一个设置产生一个地址:语言方言c ++ 98与“标准库libc ++(具有c ++ 11支持的LLVM标准库)”组合产生1,而c ++ 98与libstdc(gnu c ++标准库)组合产生一个地址;”)。您可以将C ++ 03样式的编译器explicit
与转换定义为的C ++ 11样式库结合使用,该编译器无法理解转换运算符(C ++ 11中的新增功能)operator bool()
。通过这种混合,有可能cout << cout
被解释为cout << cout.operator bool()
,而后者又简单地是cout << true
print "1"
。
正如Igor所说,您可以通过C ++ 11库获得此代码,该库std::basic_ios
使用operator bool
代替operator void*
,但是不声明(或视为)explicit
。请参阅此处以获取正确的声明。
例如,一个合格的C ++ 11编译器将给出相同的结果,
#include <iostream>
using namespace std;
int main() {
cout << "2+3 = " <<
static_cast<bool>(cout) << 2 + 3 << endl;
}
但在您的情况下,static_cast<bool>
(错误地)允许将其作为隐式转换。
编辑:由于这不是正常现象或预期行为,因此了解您的平台,编译器版本等可能会很有用。
编辑2:作为参考,该代码通常写为
cout << "2+3 = "
<< 2 + 3 << endl;
或作为
cout << "2+3 = ";
cout << 2 + 3 << endl;
并且将两种样式混合在一起,从而暴露了该错误。
出现意外输出的原因是拼写错误。你可能是说
cout << "2+3 = "
<< 2 + 3 << endl;
如果我们忽略具有预期输出的字符串,则将剩下:
cout << cout;
从C ++ 11开始,这是错误的形式。std::cout
不能隐式转换为任何std::basic_ostream<char>::operator<<
可接受的值(或非成员重载)。因此,符合标准的编译器至少必须警告您这样做。我的编译器拒绝编译您的程序。
std::cout
可以转换为bool
,并且流输入运算符的布尔重载将具有观察到的输出1。但是,该重载是显式的,因此它不应允许隐式转换。看来您的编译器/标准库的实现并不严格符合标准。
在C ++ 11之前的标准中,这格式很好。那时std::cout
有一个隐式转换运算符,void*
其中有一个流输入运算符重载。但是,输出结果会有所不同。它会打印std::cout
对象的内存地址。
所发布的代码不应针对任何C ++ 11(或更高版本的兼容编译器)进行编译,但即使在C ++ 11之前的实现上也没有警告,也应进行编译。
区别在于C ++ 11明确地将流转换为布尔值:
C.2.15第27条:输入/输出库[diff.cpp03.input.output] 27.7.2.1.3、27.7.3.4、27.5.5.4
更改:在现有的布尔转换运算符中指定显式的使用
理由:阐明意图,避免变通办法。
对原始功能的影响:依赖于隐式布尔转换的有效C ++ 2003代码将无法使用此国际标准进行编译。此类转换在以下情况下发生:
- 将值传递给带有bool类型参数的函数;
...
ostream运算符<<是使用bool参数定义的。在C ++ 11之前,由于存在向布尔转换(但不是明确的)的cout << cout
转换,因此转换为cout << true
1。
并且根据C.2.15,此代码不应再从C ++ 11开始编译。
bool
在C ++ 03中不存在转换,但是std::basic_ios::operator void*()
作为条件或循环的控制表达式,这是有意义的。
您可以通过这种方式轻松调试代码。使用时cout
输出时,将对其进行缓冲,因此您可以像这样进行分析:
想象一下第一次出现cout
代表缓冲区,而运算符<<
代表附加到缓冲区末尾。<<
在您的情况下,运算符的结果是输出流cout
。您从以下位置开始:
cout << "2+3 = " << cout << 2 + 3 << endl;
应用上述规则后,您将获得以下一系列操作:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
正如我之前所说,结果buffer.append()
是缓冲。首先,您的缓冲区为空,您需要执行以下语句:
声明: buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
缓冲: empty
首先,您需要buffer.append("2+3 = ")
将给定的字符串直接放入缓冲区并变为buffer
。现在,您的状态如下所示:
声明: buffer.append(cout).append(2 + 3).append(endl);
缓冲: 2+3 =
之后,您继续分析您的语句,然后遇到cout
作为参数追加到缓冲区末尾的情况。将cout
被视为1
这样你将追加1
到你的缓冲区的末尾。现在您处于这种状态:
声明: buffer.append(2 + 3).append(endl);
缓冲: 2+3 = 1
缓冲区中的下一件事是2 + 3
,由于加法的优先级高于输出运算符,因此您将首先将这两个数字相加,然后将结果放入缓冲区。之后,您得到:
声明: buffer.append(endl);
缓冲: 2+3 = 15
最后,将值of添加endl
到缓冲区的末尾,您将拥有:
声明:
缓冲: 2+3 = 15\n
在此过程之后,缓冲区中的字符会从缓冲区中一张一张地打印到标准输出中。因此,代码的结果是2+3 = 15
。如果您查看此内容1
,则cout
可以尝试打印其他内容。通过<< cout
从语句中删除,您将获得所需的输出。
cout << cout
产生1
?” ,而您刚刚断言它是在有关插入运算符链接的讨论中进行的。
;
在第一条输出行的末尾使用分号,而不是<<
。您没有打印您认为要打印的内容。您正在做cout << cout
,它会打印1
(cout.operator bool()
我认为它使用)。然后,紧接着5
(from2+3
),使其看起来像数字15。