3

I was trying to add some debug output into a C++03 project and got some strange result. Here's the simplified test code:

#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";
}

For some reason, here's what I get after compilation:

$ g++ test.cpp -o test && ./test && cat /tmp/test.txt
hello ... OK, this works
0x80487fe ... no, I mean hello!

Why do I get a hex number in the case of outputting a string to unnamed std::ofstream object? And why does the subsequent output of a second string work?

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • Possible duplicate: [std::ostringstream printing the address of the c-string instead of its content](https://stackoverflow.com/questions/8287188/stdostringstream-printing-the-address-of-the-c-string-instead-of-its-content) – NathanOliver Jul 13 '17 at 16:09

1 Answers1

8

The usual operator<< which we use for passing C strings to std::ostream is declared as a free function

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

The unnamed std::ofstream object is a temporary variable, and temporaries can't bind to a nonconst reference, thus this operator overload doesn't participate in overload resolution. Instead, the closest match is taken, a member function

std::basic_ostream& std::basic_ostream::operator<<(const void*);

, which takes a type-erased pointer and just prints its value. Since member functions can be called with the object being a temporary, this one does work out. This explains the hex number in output. Now, this operator returns a reference, std::basic_ostream&. As this is no longer a temporary object and is instead a reference to some nonconst object, the usual free-function overload of operator<<, which takes const char*, can be successfully called. This is why the second string is printed as expected.

Note that since C++11 the code will work as expected, since there we have an additional overload of operator<<, which takes an rvalue reference:

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

, and the temporary does bind to rvalue reference.

To make the code work in C++03, you can use a member function std::ostream::flush(), which returns a nonconst reference to the object and doesn't have any user-visible side effects on the fstream object:

#include <fstream>

int main()
{
    std::ofstream("/tmp/test.txt").flush() << "hello"
                                           << " ... OK, this now works too\n";
}
Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • You might mention, just for clarity, that some of the stream inserters are member functions and some are free functions. It's the member functions that can be called on temporaries, and that's the difference between the first two that you mention. – Pete Becker Jul 13 '17 at 16:27