为什么cout在这段代码中打印“2 + 3 = 15”?

为什么下面的程序的输出是什么?

#include <iostream> using namespace std; int main(){ cout << "2+3 = " << cout << 2 + 3 << endl; } 

产生

 2+3 = 15 

而不是预期的

 2+3 = 5 

这个问题已经进行了多次closures/重新打开循环。

在投票结束之前,请考虑关于这个问题的这个meta讨论 。

无论是有意还是无意,你都有<<在第一条输出线的末尾,你可能意思的地方; 。 所以你基本上有

 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"

事实certificate,这可能是令人惊讶的,微妙的。 std::cout ,通过它的基类std::basic_ios ,提供了一个特定的types转换运算符 ,用于在布尔上下文中使用,如

 while (cout) { PrintSomething(cout); } 

这是一个很不好的例子,因为输出很难失败 – 但是std::basic_ios实际上是input和输出stream的基类,而对于input来说,它更有意义:

 int value; while (cin >> value) { DoSomethingWith(value); } 

(在stream结束时出现循环,或者stream字符不构成有效整数时)。

现在,这个转换运算符的确切定义已经在标准的C ++ 03和C ++ 11版本之间改变了。 在较老的版本中,它是operator void*() const; (通常实现为return fail() ? NULL : this; ),而在newer中则是explicit operator bool() const; (通常简单实现为return !fail(); )。 这两个声明在布尔上下文中都可以正常工作,但是当在这种上下文之外使用(mis)时,其行为会有所不同。

特别是在C ++ 03规则下, cout << cout将被解释为cout << cout.operator void*()并打印一些地址。 在C ++ 11规则下, cout << cout根本不应该编译,因为运算符声明为explicit ,因此不能参与隐式转换。 这实际上是变化的主要动机 – 防止无意义的代码编译。 符合任一标准的编译器不会生成打印"1"的程序。

显然,某些C ++实现允许混合和匹配编译器和库以产生不一致结果的方式(引用@StephanLechner:“我在xcode中find一个产生1的设置,另一个设置产生一个地址:Language dialect c ++ 98结合“标准库libc ++(支持c ++ 11的LLVM标准库)”产生1,而c ++ 98与libstdc(gnu c ++标准库)结合产生一个地址;“)。 你可以有一个C ++ 03风格的编译器,它不理解explicit转换运算符(这是C ++ 11中的新增function),并与一个将operator bool()定义为转换的C ++ 11样式库相结合。 有了这样的混合, cout << cout可以被解释为cout << cout.operator bool() ,而cout << true并打印"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; 

它将两种风格混合在一起,揭露了这个bug。

意外输出的原因是一个错字。 你可能的意思

 cout << "2+3 = " << 2 + 3 << endl; 

如果我们忽略具有预期输出的string,我们将留下:

 cout << cout; 

自C ++ 11以来,这是不合格的。 std::cout不能隐式转换为std::basic_ostream<char>::operator<< (或非成员重载)接受的任何东西。 因此符合标准的编译器至less要警告你这样做。 我的编译器拒绝编译你的程序。

std::cout将被转换为bool ,并且streaminput操作符的bool重载将具有观察到的输出1.但是,该重载是显式的,所以它不应该允许隐式转换。 看来你的编译器/标准库的实现不严格地符合标准。

在一个预先的C ++ 11标准中,这是很好的形成的。 当时std::cout有一个隐式转换运算符void* ,它有一个streaminput运算符重载。 然而,输出的结果会有所不同。 它会打印std::cout对象的内存地址。

发布的代码不应该为任何C ++ 11(或更高版本的一致性编译器)编译,但它应该在C ++ 11以前的版本中没有警告。

不同之处在于C ++ 11将一个stream转换为一个bool显式:

C.2.15第27项:input输出库[diff.cpp03.input.output] 27.7.2.1.3,27.7.3.4,27.5.5.4

更改:指定在现有布尔转换运算符中使用显式
理由:明确意图,避免解决方法。
对原始function的影响:依赖于隐式布尔转换的有效C ++ 2003代码将无法使用此国际标准进行编译。 这种转换发生在下列条件下:

  • 将值传递给一个函数,该函数接受一个types为bool的参数;

ostream运算符<<用bool参数定义。 作为一个转换到布尔存在(而不是明确的)是前C + + 11, cout << cout被翻译为cout << true得到1。

根据C.2.15,这不应该再从C ++ 11开始编译。

您可以通过这种方式轻松debugging您的代码。 当你使用cout你的输出被缓冲,所以你可以像这样分析它:

设想一下, cout第一次出现代表缓冲区,运算符<<代表追加到缓冲区的末尾。 运算符<<结果是输出stream,在你的情况下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);

缓冲区:

首先你有buffer.append("2+3 = ") ,它将给定的string直接放入缓冲区并成为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   =   1 5

最后你把endl值加到缓冲区的末尾,你有:

声明:

缓冲区: 2 + 3   =   1 5 \ n

经过这个过程之后,缓冲区中的字符将逐个从缓冲区打印到标准输出。 所以你的代码的结果是2+3 = 15 。 如果你看看这个,你会得到额外的1你试图打印。 通过从您的声明中删除<< cout你将得到所需的输出。