Question

Consider the following minimal example:

#include <iostream>

using namespace std;

class myostream : public ostream {
    public:
        myostream(ostream const &other) :
            ostream(other.rdbuf())
        { }
};

int main() {
    cout << "hello world" << endl;

    myostream s(cout);
    s << "hello world" << endl;

    myostream(cout) << "hello world" << endl;
}

The output, both on g++ and on Visual C++, is

hello world
hello world
0x4012a4

The version that writes to a temporary object, myostream(cout), appears to prefer the member operator ostream::operator<<(void *), instead of the free operator operator<<(ostream &, char *). It seems to make a difference whether or not the object has a name.

Why does this happen? And how do I prevent this behaviour?

Edit: Why it happens is now clear from various answers. As to how to prevent this, the following seems appealing:

class myostream : public ostream {
    public:
        // ...
        myostream &operator<<(char const *str) {
            std::operator<<(*this, str);
            return *this;
        }
};

However, this results in all kinds of ambiguities.

Was it helpful?

Solution

rvalues can't be bound to non-const reference. So in your example the temporary of type ostream can't be the first argument of free operator<<(std::ostream&, char const*) and what is used is the member operator<<(void*).

If you need it, you can add a call such as

myostream(cout).flush() << "foo";

which will transform the rvalue into a reference.

Note that in C++0X, the introduction of rvalue reference will allow to provide overload of operator<< taking rvalue references as parameter, solving the root cause of the issue.

OTHER TIPS

If an object doesn't have a name (i.e. it is a temporary), it cannot be bound to a non-const reference. Specifically, it can't be bound to the first parameter of:

operator<<(ostream &, char *)

I just realized part of the answer. The temporary is not an lvalue, so it cannot be used as an argument of type ostream &.

The question "how can I make this work" remains...

Since none of the answers so far seem to give a clean solution, I will settle for the dirty solution:

myostream operator<<(myostream stream, char const *str) {
    std::operator<<(stream, str);
    return stream;
}

This is only possible because myostream has a copy constructor. (Internally, it is backed by a ref-counted std::stringbuf.)

While C++11 does resolve this issue since there are rvalue references, I think this might be a workaround for pre-C++11.

The solution is to have a member function << operator where we can cast to a non-const reference to the base class:

class myostream : public ostream {
    public:
        // ...
        template<typename T>
        ostream &operator<<(const T &t) {
            //now the first operand is no longer a temporary,
            //so the non-member operators will overload correctly
            return static_cast<ostream &>(*this) << t;
        }
};

Well, I don't know the C++ spec that causes this, but it is easy to suss out why it happens.

A temporary lives on the stack, usually to be passed to another function or to have a single operation called on it. So, if you call the free operator on it:

operator<<(myostream(cout))

It is destroyed at the end of this operation and the second "<<" operator to append the endl would reference an invalid object. The return value from the free "<<" operator would be a reference to a destructed temporary object. The C++ spec probably defines rules about free operators to prevent this scenario from frustrating and confusing C++ programmers.

Now, in the case of a "<<(void*)" member operator on the temporary, the return value is the object itself, which is still on the stack and not destroyed, so the compiler knows not to destruct it but to pass it to the next member operator, the one that takes the endl. Operator chaining on temporaries is a useful feature for succinct C++ code, so I'm sure the C++ spec designers considered it and implemented the compiler to support it intentionally.

edit

Some have said that it is to do with a non-const reference. This code compiles:

#include <iostream>
using namespace std;
class myostream : public ostream { 
    public: 
        myostream(ostream const &other) : 
            ostream(other.rdbuf()) 
        { } 
            ~myostream() { cout << " destructing "; }
    }; 
int _tmain(int argc, _TCHAR* argv[])
{
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works");
    std::operator << (result, "illegal");
         return 0;
}

And it returns

  This works destructing illegal
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top