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 anstream&(well, formally, abasic_ostream<charT>&), and they won't work with your type. Which is whyc << 'a'is okay (the inserter forcharis a member ofostream), andc << "Hello, world!"doesn't (the inserter forchar*is not a member function; it requires anstream&on its left-hand side). And inc << 'a' << "Hello, world!", the return type of thec << 'a'sub-expression isostream&, so the next inserter sees anostream&rather than aMyClass, so it's okay at that point.Your workaround avoids the problem for
char*, but won't help with other types such asstd::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
ostreamyou have to implement it as anostream, starting out with public inheritance, and implementing the appropriate overrides.