Implicit conversion to std::string [duplicate]

2020-03-09 09:26发布

问题:

This question already has answers here:
Closed 7 years ago.

Possible Duplicate:
Overload resolution failure when streaming object via implicit conversion to string

I 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 cast std::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 use operator char *.

回答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);


回答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.



回答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.



回答4:

I think the operator you need to override is "<<" .



回答5:

You need to override the operator<< method.

std::ostream & operator <<(std::ostream & os, const Test & t) {
    return os << std::string(t);
}