0

I would like to understand how the compiler is selecting constructor in the following situation

class Y
{
public:
    Y(int) {}
};

class X
{
public:
    X(int, int, Y) { cout << "Y\n"; }
    //explicit X(int, int, int) { cout << "3 ints\n"; }
};


int main()
{
    X x({ 1, 2, 3 });  // Y
}

so that if we uncomment the explicit constructor, there will be no suitable constructor to call.

CPPL
  • 726
  • 1
  • 10

1 Answers1

2

X x({ 1, 2, 3 }); is direct initialization.

The behavior of your program can be explained using copy initialization's documentation notes which states:

In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.

(emphasis mine)


And since in your given example there is no implicit conversion(as you've made that constructor explcit) from the initializer list, you get the mentioned error saying:

error: converting to ‘X’ from initializer list would use explicit constructor ‘X::X(int, int, int)’

You can resolve this error by removing the explicit used for the concerned constructor X::X(int, int, int) as then there will be an implicit conversion available from the initializer list.


but why can't X x({ 1, 2, 3 }); keep using X::X(int, int, Y) when X::X(int, int, int) is explicit?

Because the explicit constructor X::X(int, int, int) that we provide also take part in overload resolution and it is a better match than the other constructor X::X(int, int, Y). And since it is a better match it is preferred but since it is explicit, we get the mentioned error.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • Thank you for your answer, may I ask but why can't `X x({ 1, 2, 3 });` keep using `X::X(int, int, Y)` when `X::X(int, int, int)` is `explicit`? – CPPL Jun 02 '22 at 11:10
  • 2
    @CPPL Because the `explicit` constructor `X::X(int, int, int)` that we provide also take part in overload resolution and it is a better match than the other constructor `X::X(int, int, Y)`. And since it is a better match it is preferred but since it is explicit, we get the mentioned error. – Jason Jun 02 '22 at 11:28
  • Ahh that's the point. I thought the compiler should be smart enough to "reconsider" `X::X(int, int, Y)` as the better match since the compiler has all the information: the compiler knows that `X x({ 1, 2, 3 });` would need to use `X::X(int, int, int)` implicitly and it also knows that `X::X(int, int, int)` is declared as `explicit`. So I thought the compiler could somehow "go back" to change the preference... – CPPL Jun 02 '22 at 11:39
  • @CPPL Related, more or less: https://stackoverflow.com/questions/14085620/why-do-c11-deleted-functions-participate-in-overload-resolution – Paul Sanders Jun 02 '22 at 12:08
  • @CPPL I have added this comment discussion in my answer too as many times comments get deleted by moderators or by user themselves(accidentally too). Now, all necessary information is in the answer. – Jason Jun 02 '22 at 12:33
  • Thanks! May I further ask, assume there is no optimization, is `X x({ 1, 2, 3 });` calling the move constructor or the copy constructor? (defined implicitly by the compiler) – CPPL Jun 02 '22 at 12:38
  • @CPPL That should be a new question. – Paul Sanders Jun 02 '22 at 12:43
  • @CPPL It depends on the C++ version you're using. Feel free to ask a separate question for that. So that a more detailed answer can be given for that than can be given here in the comment section. – Jason Jun 02 '22 at 12:44
  • 1
    @PaulSanders You're right, I'll post another question for that. Btw, thank you very much for the related link – CPPL Jun 02 '22 at 12:46
  • 1
    @AnoopRana Yes, rule 1a of Fight Club: don't answer in the comments. – Paul Sanders Jun 02 '22 at 13:09
  • Here is the new [question](https://stackoverflow.com/q/72477155/17172007) :) – CPPL Jun 02 '22 at 13:33