为什么输出一个字符串到未命名的std :: ofstream而不是给我一个十六进制数?

Rus*_*lan 3 c++ string fstream ofstream

我试图将一些调试输出添加到C++ 03项目中并得到一些奇怪的结果.这是简化的测试代码:

#include <fstream>

int main()
{
    {
        std::ofstream file("/tmp/test.txt");
        file << "hello" << " ... OK, this works\n";
    }
    std::ofstream("/tmp/test.txt",std::ios_base::app) << "hello"
                                                      << " ... no, I mean hello!\n";
}
Run Code Online (Sandbox Code Playgroud)

出于某种原因,这是我在编译后得到的:

$ g++ test.cpp -o test && ./test && cat /tmp/test.txt
hello ... OK, this works
0x80487fe ... no, I mean hello!
Run Code Online (Sandbox Code Playgroud)

为什么在将字符串输出到未命名std::ofstream对象的情况下会得到十六进制数?为什么第二个字符串的后续输出有效?

Rus*_*lan 8

operator<<我们用于传递C字符串的常用方法std::ostream声明为自由函数

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

未命名的std::ofstream对象是临时变量,临时变量不能绑定到nonconst引用,因此此运算符重载不参与重载决策.而是采用最接近的匹配,即成员函数

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

,它采用类型擦除指针,只打印其值.由于可以在对象是临时对象的情况下调用成员函数,因此可以解决这个问题.这解释了输出中的十六进制数.现在,该运算符返回一个引用std::basic_ostream&.由于这是不再临时对象和是代替一些非const对象的引用,通常的自由功能过载operator<<,这需要const char*,可以成功地调用.这就是为什么第二个字符串按预期打印的原因.

请注意,由于C++ 11代码将按预期工作,因为我们有一个额外的重载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)

,临时确实绑定到右值引用.

要使代码在C++ 03中工作,可以使用成员函数std::ostream::flush(),该函数返回对象的nonconst引用,并且对该对象没有任何用户可见的副作用fstream:

#include <fstream>

int main()
{
    std::ofstream("/tmp/test.txt").flush() << "hello"
                                           << " ... OK, this now works too\n";
}
Run Code Online (Sandbox Code Playgroud)