o流链接,输出顺序

ostream chaining, output order

本文关键字:输出 顺序 链接      更新时间:2023-10-16

>我有一个函数,它将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<<链接的函数的正确方法是什么?

根据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!

原因是你的 print(( 函数将在语句的其余部分之前被计算并返回对 cout 的引用,然后实际打印为指针(cout <<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;
}

另请参阅此答案,了解有关"未指定"在这种情况下实际含义的更多详细信息。

十六进制输出

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

从 C++11 开始,这个转换函数被一个显式转换函数所取代,以bool,因此尝试输出一个std::ostream对象变得格式不正确。

评估顺序

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

从 C++17 开始,算子<<操作数的计算顺序严格从左到右,重载算子的操作数与 bulit-in 一个的操作数共享相同的求值顺序(在此处查看更多详细信息(。所以你的程序将始终获得输出(假设print返回能够输出的东西(

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

现场示例

在您的语句中std::cout << "Hello, world!" << print( std::cout ) << std::endl不确定std::cout << "Hello, world!"发生在print( std::cout )之前还是之后。这就是为什么订单可能不是您所期望的。

十六进制值来自你也在做std::cout << std::cout的事实(print返回std::cout,它被输入到<<链中(。右侧std::cout将转换为void *并打印到输出中。

这将起作用,将print<<相结合并控制顺序:

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

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