What language rules permit C++11 to deduce that this is an initializer_list of pairs?
c++, c++11, initializer-list, language-lawyer, list-initialization
Solution
In the expression
std::map<std::string, int> myMap = {
{ "One", 1 },
{ "Two", 2 },
{ "Three", 3 }
};
on the right side you have a braced-init-list where each element is also a braced-init-list. The first thing that happens is that the initializer list constructor of `std::map` is considered.
map(initializer_list<value_type>,
const Compare& = Compare(),
const Allocator& = Allocator());
`map<K, V>::value_type` is a typedef for `pair<const K, V>`, in this case `pair<const string, int>`. The inner braced-init-lists can be successfully converted to `map::value_type` because `std::pair` has a constructor that takes references to its two constituent types, and `std::string` has an implicit conversion constructor that takes a `char const *`.
Thus the initializer list constructor of `std::map` is viable, and the construction can happen from the nested braced-init-lists.
The relevant standardese is present in §13.3.1.7/1 [over.match.list]
When objects of non-aggregate class type `T` are list-initialized (8.5.4), overload resolution selects the constructor in two phases: — Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class `T` and the argument list consists of the initializer list as a single argument. — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class `T` and the argument list consists of the elements of the initializer list.
The first bullet is what causes the `initializer_list` constructor of `map` to be selected for the outer braced-init-list, while the second bullet results in the selection of the correct `pair` constructor for the inner braced-init-lists.
Problem
In C++11, it seems like it's legal to initialize a `std::map<std::string, int>` as follows: ``` std::map<std::string, int> myMap = { { "One", 1 }, { "Two", 2 }, { "Three", 3 } }; ``` Intuitively, this makes sense - the brace-enclosed initializer is a list of pairs of strings, and `std::map<std::string, int>::value_type` is `std::pair<std::string, int>` (possibly with some `const` qualifications. However, I'm not sure I understand how the typing works here. If we eliminate the variable declaration here and just have the brace-enclosed initializer, the compiler wouldn't know that it was looking at a `std::initializer_list<std::pair<std::string, int>>` because it wouldn't know that the braced pairs represented `std::pair`s. Therefore, it seems as though the compiler is somehow deferring the act of assigning a type to the brace-enclosed initializer until it has enough type information from the `std::map` constructor to realize that the nested braces are for pairs. I don't remember anything like this happening in C++03; to the best of my knowledge, the type of an expression never depended on its context. What language rules permit this code to compile correctly and for the compiler to determine what type to use for the initializer list? I'm hoping for answers with specific references to the C++11 spec, since it's really interesting that this works! Thanks!