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

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

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

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

为什么会这样?

顺便说一句,此行为仅发生在 C++98 版 (ideone), which I have to use. With C++11 (coliru) and 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);

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

在 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 和 returns os,本质上是对左值流执行输出操作。

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() 无法绑定到左值引用。在那里,唯一可行的重载是 member operator<<:

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

打印地址。

相关事实:

  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。现在 this 重载在 Logger() << "foo" 中获胜,并转发参数,就好像流是左值一样。然后调用之前给的free函数