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

时间:2015-04-24 23:18:29

标签: c++ c++98

以下代码段是我使用的记录器的简化版本。它扩展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;
}

3 个答案:

答案 0 :(得分:19)

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

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及更高版本中,库为rvalue流提供了一个特殊的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 :(得分: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);
    
  3. 请注意,第一个参数是非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);
    

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

答案 2 :(得分:5)

C ++ 11添加了non-member operator<<:#/ p>的重载

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 );

打印地址。