当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么cout在这段代码中打印"2+3=15"?》经验,为你挑选了5个好方法。

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

#include 
using namespace std;

int main(){

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

产生

2+3 = 15

而不是预期的

2+3 = 5

这个问题已经多次关闭/重新开启.

在投票结束之前,请考虑有关此问题的元讨论.



1> Igor Tandetn..:

无论是有意还是偶然,您都可以<<在第一个输出线的末尾,您可能意味着;.所以你基本上有

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并且打印"1".


我并没有试图在C++ 03和C++ 98之间挑剔; 关键是libc ++只是C++ 11而且只是更新的; 它不会尝试符合C++ 98/03.

2> Useless..:

正如伊戈尔所说,你得到的是一个C++ 11库,其中std::basic_ios包含operator bool而不是operator void*,但不知何故未被声明(或被视为)explicit.请参阅此处以获取正确的声明.

例如,符合标准的C++ 11编译器将给出相同的结果

#include 
using namespace std;

int main() {
    cout << "2+3 = " << 
    static_cast(cout) << 2 + 3 << endl;
}

但在你的情况下,static_cast(错误地)允许作为隐式转换.


编辑:由于这不是通常或预期的行为,因此了解您的平台,编译器版本等可能很有用.


编辑2:作为参考,代码通常写为

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

或者作为

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

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


现在我也在这样做,它必须具有传染性.谢谢!

3> eerorika..:

意外输出的原因是拼写错误.你可能意味着

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

如果我们忽略具有预期输出的字符串,我们留下:

cout << cout;

从C++ 11开始,这是不正确的.std::cout不能隐式转换为std::basic_ostream::operator<<(或非成员重载)接受的任何内容.因此,符合标准的编译器必须至少警告您这样做.我的编译器拒绝编译你的程序.

std::cout可以转换为bool,并且流输入运算符的bool重载将具有观察到的输出1.但是,该重载是显式的,因此它不应允许隐式转换.您的编译器/标准库实现似乎并不严格符合标准.

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



4> Serge Balles..:

发布的代码不应该为任何C++ 11(或后来的符合编译器)编译,但是它应该在C++ 11之前的实现中编译时甚至没有警告.

区别在于C++ 11将流转换为bool显式:

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 operator <<是用bool参数定义的.因为转换为bool存在(并且不明确)是预C++ 11,cout << cout被转换为cout << true产生1.

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


在C++ 03中没有转换为`bool`,但是有一个`std :: basic_ios :: operator void*()`,它作为条件或循环的控制表达式是有意义的.

5> 小智..:

您可以通过这种方式轻松调试代码.当您使用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

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

声明:

缓冲: 2+3 = 15\n

在此过程之后,缓冲区中的字符将从缓冲区逐个打印到标准输出.所以你的代码的结果是2+3 = 15.如果你看一下这个你得到更多的1cout你试图打印.通过<< cout从语句中删除,您将获得所需的输出.


虽然这一切都是正确的(并且格式精美),但我认为这是在乞求这个问题.我相信这个问题归结为_"为什么'的cout << cout`农产品`1`摆在首位?" _,和你刚刚宣称,它在上插入运算符链接的讨论中间.
推荐阅读
Gbom2402851125
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有