0

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

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Silver Flash
  • 871
  • 3
  • 7
  • 16
  • Perhaps worth noting that `clang-cl` (from withing VS 2019) has no problem with your *original* code. – Adrian Mole Feb 05 '20 at 12:53
  • what?? How do I change my compiler within visual studio. Im using vs 2017. I'm assuming it has to be some sort of bug in the compiler as opposed to an obscure c++ standard – Silver Flash Feb 05 '20 at 12:56
  • Not sure how to get `clang` into VS2017 (but I believe it *can* be done). But, for VS2019, see [here](https://devblogs.microsoft.com/cppblog/clang-llvm-support-in-visual-studio/) - it may help. – Adrian Mole Feb 05 '20 at 13:00

2 Answers2

3

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

Object object
  • 1,939
  • 10
  • 19
  • Does that not imply a class could only implement a single conversion? In other words, how could I tell when such an ambiguity occurs if there is any. – Silver Flash Feb 05 '20 at 13:26
  • See my updated edit on implicit conversion and the explicit keyword. Bugs like this are the reason it is recommended practice to _always_ make conversion operators and conversion constructors explicit – Object object Feb 05 '20 at 13:29
  • many thanks! Although, it would be hard to remove my C habbits, I will try. – Silver Flash Feb 05 '20 at 13:39
2

There is an ambiguity between the way an object of the type dummy can be created.

The first one is when the operator dummy is called creating a temporary object of the type dummy and then the copy constructor is called due to the explicit cast conversion.

The second one is when the object of the type myClass is converted to the type int due to the operator int and then the conversion constructor of the class myClass is called to create an object of the type dummy.

Here is a demonstrative program that shows the two ways of creating an object of a class using the explicit casting.

#include <iostream>

struct A
{
    explicit A( int x = 0 ) : x( x ) { std::cout << "A( " << x << " )\n"; }
    A( const A & ) { std::cout << "A( const A & )\n"; }
    int x;
};

int main() 
{
    A a( 1 );

    std::cout << '\n';

    ( A )a;

    std::cout << '\n';

    ( A )2;


    return 0;
}

The program output is

A( 1 )

A( const A & )

A( 2 )

Relative to your program then in your program there is one additional step before using these ways: either an object of the type myClass will be preliminary converted to the type dummy or to the type int.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335