Streaming a stringstream within a stringstream Code compiles for GCC until V5.5 but not the following versions

80 Views Asked by At

I have the following C++ code that compiles successfully for gcc < v5.5

#include <sstream>

namespace util
{

   inline void operator << (std::stringstream& stream, const char* data)
   {
      data = data ? data : "(null)";
      std::operator<<(stream, data);
   }
}

class Record
{
public:

   template<typename T>
   Record& operator<<(const T& data)
   {
      using namespace util;
      m_message << data;
    //   std::cout << data << std::endl;
      return *this;
   }
   
private:
   std::stringstream m_message;
};

int main()
{   
    Record r;
    std::stringstream m;
    r << m; //Error after gcc5.5
    r << m.str(); //Ok in both
    return 0;
    
}

Looks like passing a stringstream to a stringstream is no longer valid. I'm not sure why.

Here's a link for comparison: https://godbolt.org/z/novWE3so8

1

There are 1 best solutions below

0
Turtlefight On BEST ANSWER

This does not depend on the gcc version, but rather on the C++-Standard Version you're compiling for.

Your example works without problems with the latest gcc version when compiling for C++03: godbolt


The reason why this compiles at all is that pre-C++11 there was no bool datatype.
Checking if a stream is valid (e.g. if(stream) { /*...*/ }) therefore was mandated to just return some unspecified boolean-like type:

cppreference

operator /* unspecified-boolean-type */() const; // (until C++11)
explicit operator bool() const; // (since C++11)

Note that the conversion also was implicit pre C++11, since explicit conversion functions were added in C++11.

libstdc++ happens to define the conversion operator as follows: github

///@{
/**
 *  @brief  The quick-and-easy status check.
 *
 *  This allows you to write constructs such as
 *  <code>if (!a_stream) ...</code> and <code>while (a_stream) ...</code>
 */
#if __cplusplus >= 201103L
      explicit operator bool() const { return !this->fail(); }
#else
      operator void*() const { return this->fail() ? 0 : const_cast<basic_ios*>(this); }
#endif

Note that this allows an implicit (!) conversion from any stream to void*.

Which is unfortunate, given that std::basic_ostream provides an overload for outputting pointers:
cppreference

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

So what actually happens in your example is that the stream is implicitly converted to a void* pointer, and then operator<<(const void*) is called to output that pointer.

Which is probably not what you wanted to happen:
godbolt

#include <iostream>
#include <sstream>

int main() {
    std::stringstream m;
    m << "foobar";

    std::stringstream ss;
    // equivalent to  ss << static_cast<void*>(&m);
    ss << m;

    std::cout << ss.str() << std::endl;
    // will print the address of m, e.g. 0x7ffcda4c2540
    // NOT foobar!
    return 0;
}

The correct way to append a stream

C++ std::istreams can be written to std::ostreams by just passing the underlying stream buffer instead of the whole stream, e.g.:
godbolt

#include <iostream>
#include <sstream>

int main() {
    std::stringstream m;
    m << "foobar";

    std::stringstream ss;
    ss << m.rdbuf(); // append everything from m to ss

    std::cout << ss.str() << std::endl;
    return 0;
}

So your example could be fixed like this:

// r << m;
r << m.rdbuf();