3

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;
}
  1. If both are non-explicit. I get a compiler error saying it is ambiguous for the first expression. Second expression calls the constructor.
  2. If only operator is explicit, both conversions use the constructor.
  3. If only constructor is explicit, second conversion calls the constructor and first one uses the operator.
  4. If both are explicit, I can only compile second expression. It uses the constructor.

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.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
jannarc
  • 93
  • 2
  • 8

1 Answers1

2

Firstly, c-style cast performes static_cast, then

(emphasis mine)

1) If there is an implicit conversion sequence from expression to new_type, or if overload resolution for a direct initialization of an object or reference of type new_type from expression would find at least one viable function, then static_cast<new_type>(expression) returns the imaginary variable Temp initialized as if by new_type Temp(expression);, which may involve implicit conversions, a call to the constructor of new_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,

  1. 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.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • @Aconcagua It's only preferred for the 2nd expression (the c-ctyle cast). So in the 1st scenario the 1st expression would lead to ambiguity error, the 2nd expression would use the conversion constructor. – songyuanyao Aug 06 '19 at 11:23
  • @Aconcagua I added explanations at last, the 1st expression in the 4th scenario fails because it requires implicit conversion but both the conversion constructor and conversion operator are marked as `explicit`. Is that clear now? – songyuanyao Aug 06 '19 at 11:37
  • You're right, in the first scenario only first expression leads to ambiguity (I haven't noticed it before) – jannarc Aug 06 '19 at 11:42
  • But considering all this, would have been more meaningful in my eyes to prefer constructor over conversion even in first scenario, first expression... Any reasons for why standard committee might have opted differently? – Aconcagua Aug 06 '19 at 11:47
  • @Aconcagua I'm not sure, and yes, I agree it's confusing that behaving differently in implicit conversion scenario and explicit conversion scenario... – songyuanyao Aug 06 '19 at 13:00