Правильно ли Clang или GCC отклоняют / принимают этот код CTAD?
Clang и GCC не согласны принять этот код.
Какое стандартное требуемое поведение?
#include <utility>
#include <iostream>
#include <vector>
int main()
{
std::vector pairs = {std::pair{1,11},{2,22}, {3,33}};
for (const auto& p: pairs) {
std::cout << p.second << std::endl;
}
}
Примечание: я знаю, что это C++, поэтому возможно, что стандарт нечеткий, но я предполагаю, что одно поведение правильное.
1 ответ
Процесс CTAD во многом определяется [over.match.class.deduct] . Вообще говоря, набор перегрузки - это все доступные конструкторы типа, а также любые руководства по выводам. И набор перегрузки разрешается в соответствии с этим правилом:
Инициализация и разрешение перегрузки выполняются, как описано в [dcl.init] и [over.match.ctor], [over.match.copy] или [over.match.list] (в зависимости от типа выполняемой инициализации)
Поскольку «тип выполняемой инициализации» определенно является инициализацией списка, мы переходим к [over.match.list] . Откуда мы получаем этот печально известный маркированный список:
Первоначально функции-кандидаты являются конструкторами списка инициализаторов ([dcl.init.list]) класса T, а список аргументов состоит из списка инициализаторов как одного аргумента.
Если жизнеспособный конструктор списка инициализаторов не найден, снова выполняется разрешение перегрузки, где все функции-кандидаты являются конструкторами класса T, а список аргументов состоит из элементов списка инициализаторов.
Это говорит нам, что
initializer-list constructors
std::initializer_list
и передав его как аргумент). Однако Clang дает явную ошибку:
note: candidate function template not viable: requires at most 2 arguments, but 3 were provided vector(initializer_list<value_type> __l,
То есть он пытается вызвать этот конструктор, как будто «список аргументов состоит из элементов списка инициализатора». Это говорит о том, что Clang пропустил первый пункт при инициализации списка через CTAD. Тот факт, что добавление скобок вокруг списка инициализации в фигурных скобках «устраняет» проблему, также предполагает, что это именно то, что происходит.
Как ни странно, это работает для простых типов, таких как список инициализаторов целых чисел. И это работает, если вы явно называете каждого участника как
pair
auto
-выявить тип
initializer_list
произведено
{std::pair{1,11}, {2,22}, {3,33}}
просто хорошо. Так что эта ошибка кажется действительно конкретной.