Является ли std::initializer_list{x, y, z} (CTAD) допустимым?

При построении std::initializer_list<U> явно, может аргумент шаблона (U) выводиться (например, с использованием метода вывода аргументов шаблона класса (CTAD))?

Другими словами, я знаю, что следующие утверждения действительны:

std::initializer_list<int> x1{1, 2, 3};
std::initializer_list<int> x2 = {1, 2, 3};
auto x3 = std::initializer_list<int>{1, 2, 3};

но следующие утверждения также действительны?

std::initializer_list x1{1, 2, 3};
std::initializer_list x2 = {1, 2, 3};
auto x3 = std::initializer_list{1, 2, 3};

Компиляторы не согласны с тем, является ли шаблон аргумента std::initializer_list можно вывести:

#include <initializer_list>

struct s {
    s(std::initializer_list<int>);
};

void f() {
    std::initializer_list x1{1, 2, 3};         // Clang ERROR; GCC OK;    MSVC OK
    std::initializer_list x2 = {1, 2, 3};      // Clang ERROR; GCC OK;    MSVC OK
    auto x3 = std::initializer_list{1, 2, 3};  // Clang ERROR; GCC OK;    MSVC OK

    s x4(std::initializer_list{1, 2, 3});      // Clang ERROR; GCC ERROR; MSVC OK
    s x5{std::initializer_list{1, 2, 3}};      // Clang ERROR; GCC OK;    MSVC OK
    s x6 = s(std::initializer_list{1, 2, 3});  // Clang ERROR; GCC OK;    MSVC OK
    s x7 = s{std::initializer_list{1, 2, 3}};  // Clang ERROR; GCC OK;    MSVC OK
    s x8 = std::initializer_list{1, 2, 3};     // Clang ERROR; GCC OK;    MSVC OK

    void g(std::initializer_list<int>);
    g(std::initializer_list{1, 2, 3});         // Clang ERROR; GCC OK;    MSVC OK
}

(Смотрите этот пример в Compiler Explorer.)

Компиляторы проверены:

  • Clang версии 7.0.0 с -std=c++17 -stdlib=libc++ и с -std=c++17 -stdlib=libstdc++
  • GCC версии 8.3 с -std=c++17
  • MSVC версия 19.16 с /std:c++17

1 ответ

Clang - единственный верный компилятор. Да, действительно.

Когда компилятор видит имя шаблона без параметров шаблона, он должен просмотреть руководства по выводу шаблона и применить их к аргументам в braced-init-list. initializer_list не имеет каких-либо явных инструкций по выводу, поэтому он использует доступные конструкторы.

Единственные общедоступные конструкторы, которые initializer_list Имеются его конструкторы копирования / перемещения и конструктор по умолчанию. Создание std::initializer_list из списка фигурных скобок не делается через общедоступные конструкторы. Это делается с помощью инициализации списка, которая выполняется только компилятором. Только компилятор может выполнить последовательность шагов, необходимых для его создания.

Учитывая все это, не должно быть возможности использовать CTAD на initializer_list s, если вы не копируете из существующего списка. И эта последняя часть, вероятно, объясняет, как другие компиляторы заставляют ее работать в некоторых случаях. С точки зрения дедукции, они могут вывести список фигурных скобок как initializer_list<T> сам по себе, а не как последовательность параметров, к которым нужно применить [over.match.list], поэтому руководство по выводу видит операцию копирования.

Это ошибка Clang, о чем говорится в комментарии к никогда не исправляемому ответу Николая Боласа. Подводя итог, как и любой тип класса,std::initializer_listкомпилятор предоставил руководство по дедукции [over.match.class.deduct]§1.3

Дополнительный шаблон функции, полученный, как указано выше, из гипотетического конструктора C(C), называемый кандидатом вывода копии.

Что значит std::initializer_list компилятор неявно объявляет это руководство по выводам:

template <class T>
initializer_list (initializer_list <T>) -> initializer_list <T>;

При выводе Tдля этого руководства по выводу с непустым аргументом списка инициализаторов применяется следующее правило стандарта [temp.deduct.call] §1:

Если удаление ссылок и cv-квалификаторов из P дает std::initializer_list или Pâ € ² [N] для некоторых Pâ € ² и N, и аргументом является непустой список инициализаторов ([dcl.init.list]), то вместо этого выполняется вывод для каждого элемента списка инициализаторов независимо, принимая Pâ € ² как отдельные типы параметров шаблона функции Pâ € ²i и i-й элемент инициализатора как соответствующий аргумент.

Так T следует вывести к int и поэтому вывод аргумента шаблона класса должен быть успешным.

Отказ от ответственности: я не сторонник Clang...

Другие вопросы по тегам