Инициализатор списка и вариативный конструктор

Из справки по CPP по инициализации списка:

В противном случае конструкторы T рассматриваются в два этапа:

  • Все конструкторы, которые принимают std::initializer_list в качестве единственного аргумента или в качестве первого аргумента, если остальные аргументы имеют значения по умолчанию, проверяются и сопоставляются разрешением перегрузки с одним аргументом типа std::initializer_list.

  • Если предыдущий этап не дает совпадения, все конструкторы T участвуют в разрешении перегрузки для набора аргументов, состоящего из элементов braced-init-list, с ограничением, что разрешены только не сужающие преобразования. Если на этом этапе создается явный конструктор как наилучшее соответствие для инициализации списка копирования, компиляция завершится ошибкой (обратите внимание, что при простой инициализации копированием явные конструкторы вообще не учитываются).

Таким образом, конструктор, использующий initializer_list, рассматривается первым. В противном случае каждый элемент списка рассматривается как аргумент для конструкторов. тем не мение

#include <iostream>

using namespace std;

struct A{
    template <typename... Args> A(Args... li) { cout << sizeof...(Args) << endl;}
};

int main(){

    A a = {2,3,4};

}

Результатом является 3, что указывает на то, что Args... распаковывается как int, int, int. Почему Args... не был просто преобразован в единственное число initializer_list<int>, которое, как указывалось в деталях инициализации списка, будет первым предпринятым типом конструктора?


person AntiElephant    schedule 26.11.2015    source источник


Ответы (2)


[temp.deduct.call]/1 Вывод аргумента шаблона выполняется путем сравнения каждого типа параметра шаблона функции (назовите его P) с типом соответствующего аргумента вызова (назовите его A), как описано ниже. Если удаление ссылок и cv-квалификаторов из P дает std::initializer_list<P'> для некоторого P', а аргументом является список инициализаторов (8.5.4), то вместо этого выполняется вычитание для каждого элемента списка инициализаторов, принимая P' в качестве типа параметра шаблона функции и элемент инициализатора в качестве аргумента. В противном случае аргумент списка инициализатора приводит к тому, что параметр считается невыведенным контекстом (14.8.2.5).

Акцент мой. По этой причине вывод аргумента шаблона для конструктора из аргумента типа initializer_list<int> невозможен.

person Igor Tandetnik    schedule 26.11.2015
comment
Просто для ясности: аргумент initializer_list<int> приведет к успешному выводу аргумента шаблона в A(Args...). Таким образом, первая строка моей цитаты CPP (преобразование в аргумент initialiser_list) относится к поиску конструкторов-кандидатов, не являющихся шаблонами, а ваша цитата относится к поиску функций-кандидатов шаблона с использованием вывода аргумента шаблона. Если ни шаблонная, ни нешаблонная функции не предоставляют подходящего кандидата, каждый элемент списка рассматривается как отдельный аргумент. - person AntiElephant; 27.11.2015
comment
В [dcl.init.list]/2 есть примечание: передача списка инициализаторов в качестве аргумента шаблону конструктора template<class T> C(T) класса C не создает конструктора списка инициализаторов, поскольку список инициализаторов аргумент приводит к тому, что соответствующий параметр является невыведенным контекстом (14.8.2.1). Вот что привело меня к [temp.deduct.call]. Теперь верно, что изменение main в вашей программе на std::initializer_list<int> l{2,3,4}; A a = l; компилируется. Я не уверен, почему. - person Igor Tandetnik; 27.11.2015
comment
Ах. [temp.deduct.call]/1 говорит о случае, когда аргумент представляет собой список инициализаторов (то есть последовательность значений, разделенных запятыми, заключенных в фигурные скобки), а не экземпляр std::initializer_list. Различие важно, они не взаимозаменяемы. - person Igor Tandetnik; 27.11.2015

Если вы явно предоставляете конструктор с std::initializer_list, он будет выбирать: Demo.

template <typename... Args> A(Args...) не является конструктором с первым аргументом std::initializer_list (даже если первый аргумент может быть std::initializer_list).

А в A a = {2, 3, 4} {2, 3, 4} не имеет типа. Это не std::initializer_list.

person Jarod42    schedule 27.11.2015