7

Consider the following code:

#include <sstream>
#include <iostream>

class Foo : public std::stringstream {
public:
    ~Foo() { std::cout << str(); }
};

int main()
{
    Foo foo;
    foo << "Test1" << std::endl;

    Foo() << "Test2" << std::endl;

    return 0;
}

When I execute this, it gives me:

004177FC
Test1

I do not understand why the second example gives me gibberish. The temporary should live until the entire expression is evaluated, so why does it not behave the same as the first example?

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • I do not think that the problem is with the lifetime of the temporary. From assembler I can see that it is choosing `std::basic_ostream >::operator<<(void const*)` over the `char const *` taking operator. I cannot explain it at all. I do not see why the `void const *` accepting operator would be a better match than the `char const *` accepting operator. MSVC 2010 prints both strings but I guess that could be because of some extension rather than it being more conforming. – wilx Mar 03 '11 at 12:16
  • @wilx I have answered why it possibly happens: this is a case where external overloading (not class member) and internal (class member) do make a real difference. – CashCow Mar 03 '11 at 12:20
  • @CashCow: You are totally right, I have made a testcase but I have failed to reproduce the problem. This was because I have included member operator(char const*) which was wrong. The standard class does not have it. – wilx Mar 03 '11 at 12:34

1 Answers1

8

I tested it.

I can guess that operator<< cannot bind a temporary to a non-const reference, so any externally defined operator<< functions will not work on the Foo temporary, but any class member ones will so if ostream or ostringstream has any internal operator<< members they will work.

Therefore it may be that the overload to a pointer is a member function whilst the special one for const char * is externally declared.

The non-temporary can bind to the non-const reference for the more specialist overload.

If you really need this you can workaround with a wrapper

class Foo :
{
    mutable std::ostringstream oss;
public:
  ~Foo()
  {
    std::cout << oss.str();
  }

  template<typename T>
  std::ostream&
  operator<<( const T& t ) const
  {
      return oss << t;
  }
};

Tested and works. The first operator<< will return you the underlying stream.

I tried this too but it coredumped:

class Foo : std::ostringstream
{
    Foo & nonconstref;
public:
   Foo() : nonconstref( *this ) {}
  ~Foo()
  {
    std::cout << str();
  }

  template<typename T>
  std::ostream&
  operator<<( const T& t ) const
  {
      return nonconstref << t;
  }
};

This also works:

class Foo : public std::ostringstream
{
public:
   Foo()  {}
  ~Foo()
  {
    std::cout << str();
  }

  Foo& ncref()
  {
       return *this;
  }
};

int main()
{
    Foo foo;
    foo << "Test1" << std::endl;

    Foo().ncref() << "Test2" << std::endl;

}
CashCow
  • 30,981
  • 5
  • 61
  • 92
  • 2
    Been there, done that, confirming. I also came up with workaround—just do `std::stringstream().flush() << "anything";`. The trick is, that `flush()` does nothing on a `stringstream`, but returns invocant via reference. On a side-note, since the `operator<<` returns just `std::ostream`, you'll have to `static_cast` it back to `stringstream` to get the result out of it. – Jan Hudec Mar 03 '11 at 11:52
  • I got another solution that doesn't call flush, but yes for a stringstream flushing is a no-op so works in this case. – CashCow Mar 03 '11 at 12:19
  • That is a nasty problem, thanks for the explanation. Your first solution is not an option, because the template for `operator<<` will not work for `std::endl` - this was the reason why I tried using inheritance. I guess I will go with the last solution. – Björn Pollex Mar 03 '11 at 14:09
  • The upcoming C++0x will solve this problem with an extra operator<< overload that takes an rvalue reference and returns an lvalue reference. Then the flush() trick will not be needed anymore. – Bo Persson Mar 03 '11 at 16:53
  • operator<< returns ostream& so assuming you stream at least one thing before your endl it will definitely work. I can't see offhand why it wouldn't work with endl anyway, as it just forwards what is a pointer to a function to its internal stream. – CashCow Jul 03 '14 at 12:16