Consider the following toy code:
class Y
{
public:
Y(int, int) { cout << "Y ctor\n"; }
};
class X
{
public:
//X(initializer_list<int>) { cout << "init\n"; } // 1
X(Y&&) { cout << "Y&&\n"; } // 2
X(int, int) { cout << "param\n"; } // 3
X(const X&) { cout << "X&\n"; } // 4
X(const Y&) { cout << "Y&\n"; } // 5
X(Y) { cout << "Y\n"; } // 6
X(X&&) { cout << "X&&\n"; } // 7
};
void f(const X&) { }
void f(const Y&) { }
void f(Y) { }
int main()
{
X x({1,2});
//f({ 1,2 }); // 8
}
I'm trying to understand how the compiler uses {1,2}
in X x({1,2})
. Followings are my understandings:
Uncomment line
1
In such case, I think
X x({1,2})
is an explicit call forX(initializer_list<int>)
. That is, when compiler sees{}
-list, I think it will first see it asstd::initializer_list
and a constructor that takes an argument ofinitializer_list<T>
will be the better match than all the others.Comment out line
6
,7
In such case, I think
{1,2}
is used to construct an object ofY
which is bound with an rvalue reference (i.e. compiler chooses to callX(Y&&)
). My guess here is that the compiler treats{1,2}
as some kind of rvalue when there is no constructor that takes an argument ofinitializer_list<T>
, and thus a constructor that takes an rvalue reference is preferred (in this case,X(Y&&)
is the only constructor of this kind).Update: I found that the behavior I described in this bullet point is actually a bug of MSVC. gcc and clang will report ambiguity, not calling
X(Y&&)
Comment out line
7
Ambiguity arises. Compiler reports that both
X(Y&&)
andX(Y)
match. I think this is because althoughX(Y&&)
is a better match thanX(const Y&)
andX(const X&)
, it is indistinguishable toX(Y)
. (I think the logic here is quite twisted)Possible reference.
Comment out line
2
,7
The code compiles and prints
param
. I think this timeX(const X&)
is chosen but I'm not sure why. My guess for this is that, when all the viable matches (X(const X&)
,X(const Y&)
andX(Y)
) are indistinguishable, the compiler choosesX(const X&)
becauseX x({1,2});
is constructing anX
.Update: this can also be a MSVC bug. gcc and clang will report ambiguity, not calling
X(const X&)
Comment out line
2
,7
and uncomment line8
I think this is the case where all the viable matches are indistinguishable and
f
is not a constructor ofX
, hencef(const X&)
is not treated specially, and ambiguity arises.
May I ask if my understandings are correct? (I highly doubt they are accurate)
I find that it is quite tedious to read out what constructor is invoked by things like X x({1,2});
, so I guess in real code we should try to avoid such shorthand and write code in a more explicit way...
On the other hand, if we use the same settings for class X
and Y
as above, define and call a function g
as follows:
X g()
{
return { 1,2 };
}
int main()
{
g();
}
The results always seem to be equivalent as initializing the returned temporary by X{1,2}
(i.e. call X(initializer_list<int>)
when it exists, and in all the other cases call X(int, int)
). May I ask if this is true?