Какие языковые правила позволяют C ++ 11 сделать вывод, что это initializer_list для пар?

В 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::pairs. Следовательно, создается впечатление, что компилятор каким-то образом откладывает действие по присваиванию типа инициализатору, заключенному в фигурные скобки, до тех пор, пока он не получит достаточно информации о типе из std::map конструктор, чтобы понять, что вложенные скобки для пар. Я не помню ничего подобного в C ++ 03; насколько мне известно, тип выражения никогда не зависел от его контекста.

Какие языковые правила позволяют этому коду правильно компилироваться, а компилятор определяет, какой тип использовать для списка инициализаторов? Я надеюсь на ответы с конкретными ссылками на спецификацию C ++ 11, так как это действительно интересно, что это работает!

Спасибо!

9

Решение

В выражении

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 конструктор для внутренних фигурных списков.

9

Другие решения

Это Список инициализация. Правила находятся в §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).

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector