7

I have some code which implies a type conversion, which does not compile although there is a conversion method...

class A
{
public:
    A(void) :_m(0) { }
    A(int val) : _m(val) {}
private:
    int _m;
};
class B
{
public:
    B(void) : _m(0) {}
    B(int val) : _m(val) {}
    B(const A&);
    // there is a direct conversion operator here
    operator A(void) const { return A(_m); }
    operator int(void) const { return _m; }
private:
    int _m;
};
int main()
{
    B b;
    A a = (A)b; // error C2440 here
}

Here is the error message:

error C2440: 'type cast': cannot convert from 'B' to 'A'
message : No constructor could take the source type, or constructor overload resolution was ambiguous
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
pascal
  • 3,287
  • 1
  • 17
  • 35

2 Answers2

2

From what I understand, the compiler tries several paths to interpret a = (A)b.

  • it finds the operator A
  • but it also finds the operator int on B, and the A(int) constructor which gives it a second path B => int => A...

And it does not know which to pick.

To fix the compilation, I can:

  • remove the operator int from B
  • rewrite the error line as A a = b.operator A();...
pascal
  • 3,287
  • 1
  • 17
  • 35
2

The error message means that these two operators

operator A(void) const { return A(_m); }
operator int(void) const { return _m; }

can be used in the expression

(A)b;

As a result using these conversion operators there can be used either the constructor A( int ) or the default copy constructor A( const A & ).

To make it more clear rewrite the corresponding declaration like

A a = A( b );

So whether the object b is converted to an object of the type A using the first conversion operator or to an object of the type int using the second conversion operator.

You could avoid the ambiguity declaring the operators for example like

operator A(void) const & { return A(_m); }
operator int(void) const && { return _m; }

that is for lvalues the first operator will be used and for rvalues the second operator will be used.

Here is your program with the modified operators.

#include <iostream>
class A
{
public:
    A(void) :_m(0) { }
    A(int val) : _m(val) {}
private:
    int _m;
};
class B
{
public:
    B(void) : _m(0) {}
    B(int val) : _m(val) {}
    B(const A&);
    // there is a direct conversion operator here
    operator A(void) const & { return A(_m); }
    operator int(void) const && { return _m; }
private:
    int _m;
};

int main()
{
    B b;
    A a = b; 
    A a1 = B();
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • It seems interesting, although it exceeds my knowledge of C++... but it works. I cannot seem to find a reference docs which shows the "&", "&&" in the conversion function definition.. (granted, the ones I found at Microsoft, IBM, do not seem to allow for "const" either...) – pascal Oct 16 '19 at 15:02
  • 1
    @pascal A member function can be declared like D1 ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-type – Vlad from Moscow Oct 16 '19 at 15:08