Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(involving explicit)Precedence with operator and constructor conversion

I've browsed through a lot of questions related to conversion, but it seemed none of them discussing about explicit keyword in this way. Here's the code:

struct B;
struct A{
    /*explicit*/ A(const B&){ cout << 1; }  // *1
};
struct B{
    /*explicit*/ operator A()const{ cout << 2; } // *2
};

void foo(const A &){}

int main(void){
    B b;
    foo( /*static_cast<A>*/ (b) ); // *3
}

Result: (Y: uncommented, N:commented, X: either)

# | *1 | *2 | *3 |output|  
1 |  N |  N |  N |error |
2 |  N |  N |  Y |  1   |
3 |  N |  Y |  N |  1   |
4 |  N |  Y |  Y |  1   |
5 |  Y |  N |  N |  2   |
6 |  Y |  N |  Y |  1   |
7 |  Y |  Y |  N |error |
8 |  Y |  Y |  Y |  1   |

1, 7 are errors, which is normal.(ambiguous and no auto conversion)
2 seems that constructor have higher precedence, but why?
3, 5 are easy to understand.
4 is weird since it doesn't call the explicit one. why?
6 may be due to 'explicit' or constructor having higher precedence. Which one is the reason? 8 seems that constructor have higher precedence, but why?

Could someone offer some explanations? Thanks!

like image 796
Asu Avatar asked Dec 31 '15 10:12

Asu


1 Answers

A very good question.

First of all, the explicit thing doesn't mean "this has precedence if explicit conversion is required". It means "this thing can only be invoked explicitly". So it creates some situations where it cannot be invoked, not forces it to be invoked in other situations.

Another thing to consider is that static_cast is direct initialization, while passing an argument to a function is copy initialization. Among other things, copy initialization never uses explicit constructors. Another thing to note is that direct initialization requires use of constructors for classes (explicit or not). Although that doesn't mean that a conversion can't perform direct initialization: it can be used to convert the argument of a constructor and if the constructor is a compiler-generated copy one, then it would look like the conversion function performed direct initialization (whereas in fact it was performed by the copy constructor). Try declaring a copy constructor without defining it (the disable-copy technique) and you'll see that conversion function no longer works in the direct initialization context: it'll compile, but will cause a linking error.

With that in mind:

  1. Obvious.
  2. Direct initialization requires a constructor, so it is invoked.
  3. Obvious.
  4. The same as 2, really. Declaring the conversion function explicit only prevents it from being invoked implicitly, it doesn't force its use in explicit contexts.
  5. Obvious.
  6. Again, direct initialization requires a constructor and it allows use of explicit ones.
  7. Obvious.
  8. Another direct initialization case.
like image 171
Sergei Tachenov Avatar answered Oct 13 '22 22:10

Sergei Tachenov