Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

In an answer to this question: Initializing vector<string> with double curly braces

it is shown that

vector<string> v = {{"a", "b"}};

will call the std::vector constructor with an initializer_list with one element. So the first (and only) element in the vector will be constructed from {"a", "b"}. This leads to undefined behavior, but that is beyond the point here.

What I have found is that

std::vector<int> v = {{2, 3}};

Will call std::vector constructor with an initializer_list of two elements.

Why is the reason for this difference in behavior?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
643 views
Welcome To Ask or Share your Answers For Others

1 Answer

The rules for list initialization of class types are basically: first, do overload resolution only considering std::initializer_list constructors and then, if necessary, do overload resolution on all the constructors (this is [over.match.list]).

When initializing a std::initializer_list<E> from an initializer list, it's as if we materialized a const E[N] from the N elements in the initializer list (from [dcl.init.list]/5).

For vector<string> v = {{"a", "b"}}; we first try the initializer_list<string> constructor, which would involve trying to initialize an array of 1 const string, with the one string initialized from {"a", "b"}. This is viable because of the iterator-pair constructor of string, so we end up with a vector containing one single string (which is UB because we violate the preconditions of that string constructor). This is the easy case.


For vector<int> v = {{2, 3}}; we first try the initializer_list<int> constructor, which would involve trying to initialize an array of 1 const int, with the one int initialized from {2, 3}. This is not viable.

So then, we redo overload resolution considering all the vector constructors. Now, we get two viable constructors:

  • vector(vector&& ), because when we recursively initialize the parameter there, the initializer list would be {2, 3} - with which we would try to initialize an array of 2 const int, which is viable.
  • vector(std::initializer_list<int> ), again. This time not from the normal list-initialization world but just direct-initializing the initializer_list from the same {2, 3} initializer list, which is viable for the same reasons.

To pick which constructor, we have to go to into [over.ics.list], where the vector(vector&& ) constructor is a user-defined conversion sequence but the vector(initializer_list<int> ) constructor is identity, so it's preferred.


For completeness, vector(vector const&) is also viable, but we'd prefer the move constructor to the copy constructor for other reasons.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...