質問

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])'
役に立ちましたか?

解決

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.

他のヒント

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<<;
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top