Implicit conversion to std::string
Solution 1
operator<<(std::basic_ostream&, std::basic_string)
is a function template and user defined conversions are not considered during template argument deduction. You need to overload operator<<
for your class.
Another option, of course, is a cast
std::cout << static_cast<std::string>(test);
Solution 2
The problem is that std::string
is a specialisation of a template, std::basic_string<char>
, and the required overload of operator<<
is itself a template:
template<class charT, class traits, class Allocator>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os,
const basic_string<charT,traits,Allocator>& str);
In order to be used for template argument deduction, a user-defined type has to be an exact match; conversions are not considered.
You will need to either provide an overload of operator<<
for your class, or explicitly convert to std::string
.
Solution 3
Generally it depends on whether the stream insertion operator <<
for the class is a concrete function or a template.
With <<
as a concrete function, the overload is found, and the conversion done (as long as it's not ambiguous):
#include <iostream>
using namespace std;
template< class CharType >
struct String {};
ostream& operator<<( ostream& stream, String<char> const& s )
{
return (stream << "s");
}
struct MyClass
{
operator String<char> () const { return String<char>(); }
};
int main()
{
cout << "String: " << String<char>() << endl;
cout << "MyClass: " << MyClass() << endl;
}
However, with <<
as a function template, the template matching finds no match, and then conversion via a user-defined operator is not attempted:
#include <iostream>
using namespace std;
template< class CharType >
struct String
{
};
template< class CharType >
ostream& operator<<( ostream& stream, String< CharType > const& s )
{
return (stream << "s");
}
struct MyClass
{
operator String<char> () const { return String<char>(); }
};
int main()
{
cout << "String: " << String<char>() << endl;
cout << "MyClass: " << MyClass() << endl; // !Oops, nyet! Not found!
}
And in your case, std::string
is really just a typedef
for std::basic_string<char>
.
Fix: define a <<
operator for your class or, if you want to avoid the header dependency (thinking build time), define a conversion to e.g. char const*
, or, simplest and what I recommend, make that conversion a named one so that it has to be invoked explicitly.
Explicit is good, implicit is bad.
Mihai Todor
Software Engineer at your service. You can find me on: Linkedin GitHub Twitter Stack Overflow tools that I created: Top N users for tag located in city / country (for answers) Top N users for tag located in city / country (for questions)
Updated on June 05, 2022Comments
-
Mihai Todor almost 2 years
Possible Duplicate:
Overload resolution failure when streaming object via implicit conversion to stringI know it's not such a good idea to do this, but I really want to know the reason why the code below does not compile (i.e. why there is "no acceptable conversion"):
#include <iostream> #include <string> class Test { public: operator std::string () const; }; Test::operator std::string () const { return std::string("Test!"); } int main () { std::string str = "Blah!"; std::cout << str << std::endl; Test test; str = test;//implicitly calls operator std::string without complaining std::cout << str << std::endl; std::cout << test;//refuses to implicitly cast test to std::string return 0; }
On Visual Studio 2010 I get this error: "
error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Test' (or there is no acceptable conversion)
"Does the
<<
operator implicitly caststd::string
to something else in order to make use of it? If yes, what operator do I need to overload in my class to make such a thing work? I refuse to believe that I would actually need to useoperator char *
. -
Pete Becker over 11 years"explicit cast" is redundant.
-
Matthieu M. over 11 yearsNot all
operator<<
are templates, but thestring
one is; it might be worth precising. -
Matthieu M. over 11 years+1 for Explicit is good and showing the template effect with a reduced test case!
-
Praetorian over 11 years@MatthieuM. Thanks, updated.
-
Mihai Todor over 11 yearsSo, perhaps a stupid question: if
std::string
is justtypedef basic_string<char, char_traits<char>, allocator<char> >
and I defineoperator std::basic_string<char, std::char_traits<char>, std::allocator<char> > () const;
inside myTest
class, why is it still not considered "an exact match"? I mean, based on what you (and the others) said, this should make it compile, right? I really would like to understand the reasoning behind it. -
Mihai Todor over 11 years"user defined conversions are not considered during template argument deduction" - Interesting. So, basically, it should compile if I define
operator std::basic_string<char, std::char_traits<char>, std::allocator<char> > () const
instead ofoperator std::string () const;
? Unfortunately, it doesn't seem to like it either. I'm just trying to understand templates better, so don't worry, this will never end up in real code :) -
Mihai Todor over 11 yearsOut of sheer curiosity, how would a user-defined type look like in order to be an exact match for the above case? I just want to understand templates better.
-
Cheers and hth. - Alf over 11 years@MihaiTodor: oh, the argument deduction tries to match
MyClass
(the actual argument type for the<<
invocation) withstd::basic_string<C, T, A>
(the formal argument type), whereC
,T
andA
are template parameters. But there is no choice of these parameters that turnsMyClass
into an exact match, or any kind of match. For template parameter matching only considers direct, exact matches, not any user defined conversions. The only "conversion" supported by the template parameter matching is derived to base. -
Lightness Races in Orbit over 11 yearsActually it's because there are template parameters participating in deduction in the desired implicit conversion.
-
Mike Seymour over 11 years@MihaiTodor: To be an exact match, it would have to be a specialisation of
std::basic_string
. -
Mihai Todor over 11 years@LightnessRacesinOrbit Thanks for pointing out the duplicate! I think it's starting to sink in now, slowly... Makes my brain hurt. I'll accept this answer, based on your comment.