Search code examples
c++type-conversionoperator-overloadinguser-defined-typesambiguity

C++ : user defined Explicit type conversion function error


Here is how the code looks:

using namespace std;
class dummy{
public:
    int x;
    explicit dummy(int x = 0) : x{ this->x = x } {}; //explicit has no effect here
};

class myClass {
public:
    operator int(); //<---problematic conversion
    explicit operator dummy();

};

myClass::operator int() {
    return 10;
}

myClass::operator dummy(){
    return dummy(9);
}

int main() {
    myClass mc1; 
    dummy val = (dummy)mc1;
    cout << "mc1 int cast: " << mc1 << endl; 
    cout << "val.x: :" << val.x << endl;
    std::cin.get();
    return 0;
}

I'm using ms vs compiler here and getting c2440 (type cast error). To my understanding, I'm not doing anything wrong syntax-wise. The problem here is that it works fine if I remove the implicit conversion: operator int() and its respective function out of the code. ie:

using namespace std;
class dummy{
public:
    int x;
    explicit dummy(int x = 0) : x{ this->x = x } {}; //explicit has no effect here
};

class myClass {
public:
    //operator int();
    explicit operator dummy();

};
/*
myClass::operator int() {
    return 10;
}*/

myClass::operator dummy(){
    return dummy(9);
}

int main() {
    myClass mc1; 
    dummy val = (dummy)mc1;
    //cout << "mc1 int cast: " << mc1 << endl; 
    cout << "val.x: " << val.x << endl;
    std::cin.get();
    return 0;
}

this will produce the following output (as expected):

val.x: 9

Edit: explicit keyword was missing in the second example. The output is the same


Solution

  • Compiling your original code with gcc-9 on ubuntu 18.04 I get this:

    test.cpp:27:24: error: call of overloaded ‘dummy(myClass&)’ is ambiguous
       27 |     dummy val = (dummy)mc1;
          |                        ^~~
    test.cpp:7:14: note: candidate: ‘dummy::dummy(int)’
        7 |     explicit dummy(int x = 0) : x{ this->x = x } {}; //explicit has no effect here
          |              ^~~~~
    test.cpp:4:7: note: candidate: ‘constexpr dummy::dummy(const dummy&)’
        4 | class dummy{
          |       ^~~~~
    test.cpp:4:7: note: candidate: ‘constexpr dummy::dummy(dummy&&)’
    

    What is happening is the compiler cannot tell if you want to create a new 'dummy' class out of the int conversion of 'myClass' or a new 'dummy' out of the implicitly generated copy operator of 'dummy'.

    This causes a loop where 'myClass' can be converted to both int and dummy which means that compiler gets stuck trying to convert 'myClass' since it doesnt know what you actually want to do - convert to 'dummy', convert to 'dummy' then copy it, or make a new 'dummy' by converting 'myClass' to int

    The solution to this is to make your conversion operator to int explicit this prevents implicit conversion by the compiler and allows this to work (at least on gcc-9).

    This is an updated copy of your code which will work:

    using namespace std;
    class dummy{
    public:
        int x;
        explicit dummy(int x=0) : x(x) {}
    
    
    };
    
    class myClass {
    public:
        explicit operator int(); // < Set to explicit
        explicit operator dummy();
    
    };
    
    myClass::operator int() {
        return 10;
    }
    
    myClass::operator dummy(){
        return dummy(9);
    }
    
    int main() {
        myClass mc1; 
        dummy val = static_cast<dummy>(mc1);
        cout << "mc1 int cast: " << static_cast<int>(mc1) << endl; 
        cout << "val.x: :" << val.x << endl;
        std::cin.get();
        return 0;
    }
    

    This compiles with gcc-9 and clang++-9

    As a side note please please please never use C style casts in C++, they are error prone and a pain in the ass to debug