Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::ostringstream operator overload search order?

I have the following class:

namespace {
class MimeLogger : public std::ostringstream
{
public:
    MimeLogger()
    {}

    ~MimeLogger()
    {
        LOGEVENT( logModuleWSE, logEventDebug, logMsgWSETrace1, str() );
    }
};
}

When I do this:

MimeLogger() << "Hello " << "World";

The first "Hello " string is treated as a void*. If I debug the code, "Hello " is passed into std::basic_ostream::operator<< (void const*) and prints as a pointer value, not a string. The second string, "World" is properly passed into the global overloaded << operator that takes a char const*.

I expect both usages of the << operator to resolve to the same overload, but this does not happen. Can someone explain, and maybe propose a fix?

Thanks in advance.

Update

I neglected to mention that I'm stuck with C++03, but I'm glad that some people covered both the C++03 and C++11 cases.

like image 304
void.pointer Avatar asked May 15 '12 23:05

void.pointer


Video Answer


1 Answers

C++03: For the expression MimeLogger() << "Hello ", the template function

template <typename charT, class traits>
std::basic_ostream<charT, traits>& std::operator<< (
    std::basic_ostream<charT, traits>& os,
    const char* cstr);

is not considered during overload resolution because the temporary MimeLogger() may not be bound to a non-const reference. The member function overloads do not have this problem because the rules for the implicit parameter do allow binding to a temporary.

If you can use a compiler with support for C++11 rvalue-references, this should work as you intended, because the C++11 library provides an additional overload

template <typename charT, class traits, typename T>
std::basic_ostream<charT, traits>& std::operator<< (
    std::basic_ostream<charT, traits>&& os,
    const T& x ); // { os << x; return os; }

which allows temporary streams to be used left of << as though they were not temporary.

(I did try a test program with g++ and got different results without and with -std=c++0x.)

If you cannot use a C++11 friendly compiler, adding this to the public section of class MimeLogger is a workaround that will do what you want with C++03:

template<typename T>
MimeLogger& operator<<(const T& x)
{
    static_cast<std::ostringstream&>(*this) << x;
    return *this;
}

using std::ostringstream::operator<<;

The using-declaration makes sure the member overloads from the standard library are also visible from MimeLogger. In particular, without it manipulators like std::endl don't work with the template operator, since std::endl is itself a function template, and that's too much template type deduction to expect from C++. But things are fine as long as we're sure not to hide the ostream member that makes the function manipulators work (27.7.3.6.3):

namespace std {
    template <typename charT, class traits>
    class basic_ostream : /*...*/ {
    public:
        basic_ostream<charT, traits>& operator<<(
            basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
    };
}
like image 101
aschepler Avatar answered Sep 21 '22 01:09

aschepler