Правильно ли 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

. И Clang может auto-выявить тип initializer_list произведено {std::pair{1,11}, {2,22}, {3,33}}просто хорошо. Так что эта ошибка кажется действительно конкретной.
Другие вопросы по тегам