Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Monday, May 01, 2006

Be careful of C++ conversion operator, road to ambiguity.

Originally by James Kanze from clcm (editted formatting)

> #include
> #include

> namespace my{
> class A{
> public:
> operator std::sring(){
> std::string rval;
> //do some work here
> return rval;
> };
> template
> operator TypeVal(){
> TypeVal rval;
> //do some work here...
> return rval;
> };

You left the above function out in your original posting. It makes a big difference.

> };
> };

> int main(){
> my::A a;
> std::string s=a; //will compile without problem

Yes. But:
std::string s( a ) ;
won't.

In your version, the compiler tries to convert a to an std::string, and then uses the copy constructor to initialize s. (The copy constructor can later be elided, but for the analysis in overload resolution, access control and all the rest, the compiler must behave as if it were called.) And there is only one way to convert a my::A to an std::string -- your conversion operator. (The compiler will favor a non-template function over a template function.)

My version, using direct initialization, looks for constructors of std::string which can be called with a single my::A argument. Because of the template conversion operator, this is in fact any constructor which can be called with a single argument; there are at least two, the copy constructor and the constructor from a char const*. Both involve a user defined conversion operator, so both are equally "good", and the call is ambiguous. (Note that in this case, the compiler will favor a non-template constructor of std::string over a templated one. But none of the constructors of std::string are templates, so this doesn't help.)

> s=a; //will not compile

Same problem as my version above. There are several assignment operators which can be called with a single argument, and my::A can be converted into just about any type of argument.

> };