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

nil*_*ils 22 c++ c++98

以下代码段是我使用的记录器的简化版本.它扩展std::ostringstream并可以使用<<-operator 填充.销毁后,所有内容都会写入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;
}
Run Code Online (Sandbox Code Playgroud)

T.C*_*.C. 19

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

template< class Traits > 
basic_ostream<char,Traits>& operator<<(basic_ostream<char,Traits>& os, const char* s);
Run Code Online (Sandbox Code Playgroud)

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

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

在C++ 11及更高版本中,库operator<<为rvalue流提供了一个特殊的:

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os, 
                                            const T& value );
Run Code Online (Sandbox Code Playgroud)

执行os << value和返回os,基本上在左值流上执行输出操作.


Bri*_*ian 11

相关事实:

  1. Logger()Logger().stream()左值,但是左值.
  2. operator<<,需要一个指针和输出其地址是一个成员ostream&,而operator<<采用一个const char*,并打印该字符串是一个免费的功能,

    template<class traits>
    basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
    const char* s);
    
    Run Code Online (Sandbox Code Playgroud)

请注意,第一个参数是非const左值引用,因此它不能绑定到右值.因此,如果流是右值,则此过载不可行.因此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);
Run Code Online (Sandbox Code Playgroud)

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


Bar*_*rry 5

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 );
Run Code Online (Sandbox Code Playgroud)

现在,你认为你正在调用的运营商Logger()是这样的:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
                                        const char* s );
Run Code Online (Sandbox Code Playgroud)

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

basic_ostream& operator<<( const void* value );
Run Code Online (Sandbox Code Playgroud)

打印地址.