Question

Problem:

#include <iostream>
#include <sstream>

class MyClass : private std::ostream
{
public :
    MyClass() : std::ostream(&mBuffer) { }

    using std::operator<<; 


private:
    std::stringbuf mBuffer;
};

// In main function, for example
MyClass c;
c << 'A' << "Hello, World!";  // Works
c << "Hello, World!" << 'A';  // ERROR

Error (MS Visual Studio 2010) is error C2666: 'std::basic_ostream<_Elem,_Traits>::operator <<' : 5 overloads have similar conversions

What am I doing wrong or is this another MS Visual Studio bug?

Workaround: Adding the following member method seems to work, but I'd like some insight into the root cause.

MyClass& operator<<(const char* str) { 
        std::ostream& os = *this;
        os << str;
        return *this; 
    }

Background: MySql++ doesn't compile with Visual Studio 2010 (see mailing list) because VS2010 doesn't support public inheritance of (among other things) std::ostream. As a work around, I'm attempting private inheritance as that's less hacking than composition. MyClass& operator<<(const char* str) { std::ostream& os = *this; os << str; return *this; }

Full error message

1>d:\repo\test\test\main.cpp(30): error C2666: 'std::basic_ostream<_Elem,_Traits>::operator <<' : 5 overloads have similar conversions
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>          d:\program files (x86)\microsoft visual studio 10.0\vc\include\ostream(206): could be 'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(std::_Bool)'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>          d:\program files (x86)\microsoft visual studio 10.0\vc\include\ostream(467): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(const void *)'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>          d:\program files (x86)\microsoft visual studio 10.0\vc\include\ostream(851): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const _Elem *)' [found using argument-dependent lookup]
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>          d:\program files (x86)\microsoft visual studio 10.0\vc\include\ostream(764): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' [found using argument-dependent lookup]
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>          d:\program files (x86)\microsoft visual studio 10.0\vc\include\ostream(679): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' [found using argument-dependent lookup]
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
1>          while trying to match the argument list '(MyClass, const char [14])'
Was it helpful?

Solution

This is the result of a little quirk in the way that stream inserters are defined. Some are members of ostream; those are the ones that get picked up by the using declaration. Some are free functions; those take an stream& (well, formally, a basic_ostream<charT>&), and they won't work with your type. Which is why c << 'a' is okay (the inserter for char is a member of ostream), and c << "Hello, world!" doesn't (the inserter for char* is not a member function; it requires an stream& on its left-hand side). And in c << 'a' << "Hello, world!", the return type of the c << 'a' sub-expression is ostream&, so the next inserter sees an ostream& rather than a MyClass, so it's okay at that point.

Your workaround avoids the problem for char*, but won't help with other types such as std::string, std::complex, or any user-defined type.

The right answer is that if you want to create a type that can stand in for an ostream you have to implement it as an ostream, starting out with public inheritance, and implementing the appropriate overrides.

OTHER TIPS

Just forward operator<< so that it does The Right Thing (TM):

class MyClass : private std::ostream
{
public :
    MyClass() : std::ostream(&mBuffer) { }

    template <typename T>
    MyClass& operator<<(T&& t)
    {
        static_cast<std::ostream&>(*this) << std::forward<T>(t);
        return *this;
    }

private:
    std::stringbuf mBuffer;
};

Indeed, some of the stream operators are declared as (friend) free functions, not as a member function of std::ostream. Using declarations won't import those.

Just to clarify, you meant

using std::ostream::operator<<;

rather than

using std::operator<<;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top