I have a piece of code where I have both conversion constructor and conversion operator.
#include <iostream>
struct ClassFloat;
struct ClassInt{
int value;
ClassInt(int c) : value(c){std::cout << "From integer\n";};
ClassInt(ClassFloat x);
//explicit ClassInt(ClassFloat x);
};
struct ClassFloat{
float val;
explicit operator ClassInt() {std::cout << "Conversion operator called\n"; return ClassInt{999};}
//operator ClassInt() { std::cout << "Conversion operator called\n"; return ClassInt{999};}
};
ClassInt::ClassInt(ClassFloat x){
std::cout << "Conversion constructor called!\n";
value = (int)x.val;
}
int main(){
ClassFloat floatObj{3.5f};
ClassInt instance1 = floatObj; // (1)
ClassInt instance2 = (ClassInt)floatObj; // (2)
return 1;
}
I didn't understand why conversion operator wasn't called at the second expression in the second scenario.
I was also expecting an ambiguity error in the fourth scenario (similar to the first scenario) but constructor was picked.
I compiled using g++ 7.4.0 with -pedantic and -std=c++17 flags.
Firstly, c-style cast performes static_cast
, then
(emphasis mine)
1) If there is an implicit conversion sequence from
expression
tonew_type
, or if overload resolution for a direct initialization of an object or reference of typenew_type
fromexpression
would find at least one viable function, thenstatic_cast<new_type>(expression)
returns the imaginary variableTemp
initialized as if bynew_type Temp(expression);
, which may involve implicit conversions, a call to the constructor ofnew_type
or a call to a user-defined conversion operator.
So given (ClassInt)floatObj;
(which is initialized as if ClassInt Temp(floatObj);
), the conversion constructor would be always preferred, it would be used directly to construct a ClassInt
. While applying the conversion operator requires an implicit conversion from floatObj
to ClassInt
(then copy-initialize the temporary ClassInt
in concept).
It seems your observed result is a little different with the above summary, for the 1st scenario,
- If both are non-explicit. I get a compiler error saying it is ambiguous.
Only the 1st expression leads to ambiguous issue, the 2nd expression would use the conversion constructor.
BTW: I tried with gcc 7.3.0, which gives the result as expected.
To answer your questions,
I didn't understand why conversion operator wasn't called at the second expression in the second scenario.
Because conversion constructor is preferred for the 2nd expression (the c-ctyle cast).
I was also expecting an ambiguity error in the fourth scenario (similar to the first scenario) but constructor was picked.
Same as above, conversion constructor is preferred for the 2nd expression; the 1st expression would lead to error because both the conversion constructor and conversion operator are marked as explicit
.
Also note that the 1st expression requires implcit conversion, then whether the conversion constructor or the conversion operator are marked as explicit
does matter. On the other hand, the 2nd expression is explicit conversion, which doesn't care that.