В C ++ 11 кажется законным инициализировать std::map<std::string, int>
следующее:
std::map<std::string, int> myMap = {
{ "One", 1 },
{ "Two", 2 },
{ "Three", 3 }
};
Интуитивно понятно, что это имеет смысл — инициализатор в скобках — это список пар строк, и std::map<std::string, int>::value_type
является std::pair<std::string, int>
(возможно, с некоторыми const
квалификации.
Однако я не уверен, что понимаю, как здесь работает набор текста. Если мы исключим здесь объявление переменной и просто заключим в скобки инициализатор, компилятор не будет знать, что он смотрит на std::initializer_list<std::pair<std::string, int>>
потому что он не знал бы, что пары в скобках представлены std::pair
s. Следовательно, создается впечатление, что компилятор каким-то образом откладывает действие по присваиванию типа инициализатору, заключенному в фигурные скобки, до тех пор, пока он не получит достаточно информации о типе из std::map
конструктор, чтобы понять, что вложенные скобки для пар. Я не помню ничего подобного в C ++ 03; насколько мне известно, тип выражения никогда не зависел от его контекста.
Какие языковые правила позволяют этому коду правильно компилироваться, а компилятор определяет, какой тип использовать для списка инициализаторов? Я надеюсь на ответы с конкретными ссылками на спецификацию C ++ 11, так как это действительно интересно, что это работает!
Спасибо!
В выражении
std::map<std::string, int> myMap = {
{ "One", 1 },
{ "Two", 2 },
{ "Three", 3 }
};
на правой стороне у вас есть приготовился-INIT-лист где каждый элемент также является списком фигурных скобок. Первое, что происходит, это то, что конструктор списка инициализатора std::map
Считается.
map(initializer_list<value_type>,
const Compare& = Compare(),
const Allocator& = Allocator());
map<K, V>::value_type
является typedef для pair<const K, V>
, в этом случае pair<const string, int>
, Внутренние списки фигурных скобок могут быть успешно преобразованы в map::value_type
так как std::pair
имеет конструктор, который принимает ссылки на два составляющих его типа, и std::string
имеет неявный конструктор преобразования, который принимает char const *
,
Таким образом, конструктор списка инициализатора std::map
является жизнеспособным, и конструкция может происходить из вложенных фигурных скобок-init-списков.
Соответствующий стандарт присутствует в §13.3.1.7 / 1 [over.match.list]
Когда объекты неагрегированного класса
T
инициализируются списком (8.5.4), разрешение перегрузки выбирает конструктор в два этапа:
— Первоначально функции-кандидаты являются конструкторами списка инициализаторов (8.5.4) классаT
и список аргументов состоит из списка инициализаторов как единственного аргумента.
— Если жизнеспособный конструктор списка инициализаторов не найден, разрешение перегрузки выполняется снова, где все функции-кандидаты являются конструкторами класса.T
и список аргументов состоит из элементов списка инициализатора.
Первая пуля это то, что вызывает initializer_list
конструктор map
быть выбранным для внешнего списка фигурных скобок, в то время как второй маркер приводит к выбору правильного pair
конструктор для внутренних фигурных списков.
Это Список инициализация. Правила находятся в §8.5.4 [dcl.init.list] / p3 стандарта:
Инициализация списка объекта или ссылки типа T определяется как
следующим образом:
- Если список инициализаторов не имеет элементов и T является типом класса с конструктором по умолчанию, объект инициализируется значением.
- В противном случае, если T является агрегатом, выполняется агрегатная инициализация (8.5.1). [пример опущен]
- В противном случае, если T является специализацией
std::initializer_list<E>
,initializer_list
объект построен как описано ниже и
используется для инициализации объекта в соответствии с правилами для
инициализация объекта из класса того же типа (8.5).- В противном случае, если T является типом класса, учитываются конструкторы. Применимые конструкторы перечислены, и выбран лучший
через разрешение перегрузки (13.3, 13.3.1.7). Если сужение
преобразование (см. ниже) требуется для преобразования любого из аргументов,
программа плохо сформирована.- [пример и остальные правила опущены]
Обратите внимание, что разрешение перегрузки предпочтет std::initializer_list
конструкторы в этих случаях (§13.3.1.7 [over.match.list]).
Таким образом, когда компилятор видит ограниченный список, используемый для инициализации объекта неагрегированногоstd::initializer_list
Тип класса, он будет выполнять разрешение перегрузки, чтобы выбрать соответствующий конструктор, предпочитая initializer_list
конструктор, если существует жизнеспособный (как это делается для std::map
).