为什么写入临时字符串流对象只打印对象地址

Why does writing into temporary string stream object only print object addresses?

本文关键字:对象 打印 地址 字符串 为什么      更新时间:2023-10-16

以下代码片段是我使用的记录器的简化版本。它扩展std::ostringstream,可以使用<<运算符填充。销毁后,所有内容将写入std::cout

Logger(),将(<<)直接写入临时对象,我希望它打印该输入,但是,它只打印std::cout上的某物地址。写入临时对象的引用时,Logger().stream() 按预期工作。

为什么会这样?

顺便说一句,这种行为只发生在我必须使用的 C++98-land (ideone) 中。使用 C++11(coliru)和 C++14(ideone),两个调用变体都按预期工作。C++11/14 有什么不同?

#include <iostream>
#include <sstream>
class Logger : public std::ostringstream
{
public:
    ~Logger()
    {
        std::cout << this->str() << std::endl;
    }
    Logger& stream()
    {
        return *this;
    }
};
int main( int argc, char ** argv )
{
    // 1.
    // Prints an address, e.g. 0x106e89d5c.
    Logger() << "foo";
    // 2.
    // Works as expected.
    Logger().stream() << "foo";
    // What is the difference between 1. and 2.?
    return 0;
}

处理const char *插入的operator<<是非成员模板:

template< class Traits > 
basic_ostream<char,Traits>& operator<<(basic_ostream<char,Traits>& os, const char* s);

它通过非常量(左值)引用获取其流,该引用不绑定到临时引用。

在 C++98/03 中,最可行的函数是成员operator<<(const void *),它打印一个地址。

在 C++11 及更高版本中,库为右值流提供了特殊operator<<

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os, 
                                            const T& value );

它执行os << value并返回os,本质上是在左值流上执行输出操作。

相关事实:

  1. Logger()是右值,但Logger().stream()是右值。
  2. 获取指针并打印其地址的operator<<ostream& 的成员,而获取const char*并打印字符串的operator<<是一个自由函数,

    template<class traits>
    basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
    const char* s);
    

请注意,第一个参数是非常量左值引用,因此它不能绑定到右值。因此,如果流是右值,则此重载不可行。因此,const char*将转换为const void*并打印其地址。使用 Logger().stream() (左值)时,此重载获胜并打印字符串。

在 C++11 中,添加了一个新的右值流插入运算符:

template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);

效果os << x.现在这个重载在Logger() << "foo"中获胜,并转发参数,就好像流是一个左值一样。然后调用之前给出的 free 函数。

C++11 添加了重载的non-member operator<<

template< class CharT, class Traits, class T >    
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
                                            const T& value );

现在,您认为在Logger()情况下要呼叫的接线员是以下接线员:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
                                        const char* s );

这适用于Logger().stream()情况,因为这是一个左值引用,但这不适用于Logger() << "foo"情况。 Logger()不能绑定到左值引用。在那里,唯一可行的重载是成员operator<<

basic_ostream& operator<<( const void* value );

打印地址。