ostream链接,输出顺序

时间:2012-01-19 18:22:06

标签: c++ iostream chaining

我有一个函数,它将ostream引用作为参数,将一些数据写入流,然后返回对同一个流的引用,如下所示:

#include <iostream>

std::ostream& print( std::ostream& os ) {
  os << " How are you?" << std::endl;
  return os;
}

int main() {
  std::cout << "Hello, world!" << print( std::cout ) << std::endl;
}

此代码的输出为:

 How are you?
Hello, world!0x601288

但是,如果我将链接表达式分成两个语句,比如

int main() {
  std::cout << "Hello, world!";
  std::cout << print( std::cout ) << std::endl;
}

然后我至少在输出中得到正确的顺序,但仍然得到一个十六进制值:

Hello, world! How are you?
0x600ec8

我想了解这里发生了什么。正常函数优先于operator<<,这就是输出顺序反转的原因吗?编写将数据插入ostream但也可以与operator<<链接的函数的正确方法是什么?

5 个答案:

答案 0 :(得分:5)

根据C ++标准,代码的行为是未指定的

说明

以下内容(为简单起见,我删除了std::endl

std::cout << "Hello, world!" << print( std::cout ); 

相当于:

operator<<(operator<<(std::cout, "Hello, World!"), print(std::cout));

这是一个函数调用,传递两个参数:

  • 第一个参数是:operator<<(std::cout, "Hello, World!")
  • 第二个论点是:print(std::cout)

现在,标准没有指定评估参数的顺序。它是未指定的。但是你的编译器似乎首先评估第二个参数,这就是为什么它打印“你好吗?”首先,将第二个参数计算为std::ostream&类型的值,然后传递给它上面显示的调用(该值是对象std::cout本身)。

为什么是十六进制输出?

你得到十六进制输出,因为第二个参数的计算结果是std::cout,它被打印为十六进制数,因为std::cout隐式转换为void*类型的指针值,这就是为什么它打印为十六进制数。

试试这个:

void const *pointer = std::cout; //implicitly converts into pointer type!
std::cout << std::cout << std::endl;
std::cout << pointer << std::endl;

它将为两者打印相同的值。例如,ideone处的此示例打印出:

0x804a044
0x804a044 

另请注意,我没有使用显式强制转换;相反std::cout 隐式转换为指针类型。

希望有所帮助。


  

编写将数据插入ostream但又可以与operator<<链接的函数的正确方法是什么?

什么时候取决于链接的意思?显然,以下内容不起作用(如上所述):

std::cout << X << print(std::cout) << Y << Z; //unspecified behaviour!

无论你如何写print()

然而,这是明确界定的:

print(std::cout) << X << Y << Z; //well-defined behaviour!

答案 1 :(得分:3)

原因是你的print()函数将在语句的其余部分之前被评估,并返回对cout的引用,然后cout实际打印为指针(cout&lt;&lt; cout)。此评估顺序实际上是未指定的行为,但您的编译器似乎就是这种情况。

至于定义一个实际上已定义具有相同功能的行为的流“”功能“,这将起作用;

#include <iostream>

template <class charT, class traits>
  std::basic_ostream<charT,traits>& print ( std::basic_ostream<charT,traits>& os )
{
        os << " How are you?" << std::endl;
        return os;
}

int main() {
  std::cout << "Hello, world!" << print << std::endl;
}

另请参阅this answer,了解更多有关“未指明”在这种情况下实际含义的细节。

答案 2 :(得分:1)

在您的陈述std::cout << "Hello, world!" << print( std::cout ) << std::endl中,std::cout << "Hello, world!"之前或之后是否print( std::cout )未定义。这就是为什么订单可能不符合您的预期。

十六进制值来自于您正在执行std::cout << std::coutprint返回std::cout并将其输入<<链)。右手std::cout将转换为void *并打印到输出中。

答案 3 :(得分:1)

这可以合并print<<并控制订单:

print( std::cout << "Hello, world!" ) << std::endl;

或者,如果你想要一个用<<调用的函数,请参阅Joachim的答案。

答案 4 :(得分:1)

十六进制输出

在C ++ 11之前,类std::ostream具有转换函数void*。由于您的print函数返回std::ostream&,因此在评估std::cout << print(...)时,返回的std::ostream左值将隐式转换为void*,然后作为指针值输出。这就是十六进制输出的原因。

从C ++ 11开始,这个转换函数is replaced显式转换函数转移到bool,因此尝试输出std::ostream对象变得不合适形成。

评估订单

在C ++ 17之前,重载运算符被认为是用于分析评估顺序的函数调用,并且未指定函数调用的不同参数的评估顺序。因此,首先评估print函数并不奇怪,这导致首先输出How are you?

从C ++ 17开始,运算符<<的操作数的评估顺序严格地从左到右,并且重载运算符的操作数与bulit-in的操作数共享相同的评估顺序(查看更多详细信息) here)。因此,您的程序将始终获得输出(假设print返回能够输出的内容)

Hello, world! How are you?
something returned by print

LIVE EXAMPLE