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
1In 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_listand a constructor that takes an argument ofinitializer_list<T>will be the better match than all the others.Comment out line
6,7In such case, I think
{1,2}is used to construct an object ofYwhich 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
7Ambiguity 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,7The 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,7and uncomment line8I think this is the case where all the viable matches are indistinguishable and
fis 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?
X x{1, 2};should also cut down on the confusion. Generally the fewer constructors you have the better.std::string!