Руководство по дедукции и вариативные шаблоны

Рассмотрим следующий код:

#include <tuple>
#include <iostream>

template <class T>
struct custom_wrapper
{
    template <class Arg>
    custom_wrapper(Arg arg): data(arg) {}
    T data;
};

template <class Arg>
custom_wrapper(Arg arg) -> custom_wrapper<Arg>;

template <class... T>
struct custom_tuple
{
    template <class... Args>
    custom_tuple(Args... args): data(args...) {}
    std::tuple<T...> data;
};

template <class... Args>
custom_tuple(Args... args) -> custom_tuple<Args...>;

int main(int argc, char* argv[])
{
    custom_wrapper<int> w1(42);  // OK
    custom_wrapper w2(42);       // OK
    custom_tuple<int> t1(42);    // OK
    custom_tuple t2(42);         // Fails
    return 0;
}

Неудачная строка возвращает следующую ошибку в g++7:

variadic_deduction_guide.cpp: In instantiation of 'custom_tuple<T>::custom_tuple(Args ...) [with Args = {int}; T = {}]':
variadic_deduction_guide.cpp:31:23:   required from here
variadic_deduction_guide.cpp:19:45: error: no matching function for call to 'std::tuple<>::tuple(int&)'
     custom_tuple(Args... args): data(args...) {}

Это нормально или это ошибка компилятора?


person Vincent    schedule 02.06.2017    source источник


Ответы (1)


Это ошибка gcc 80871. Далее следует объяснение того, почему код сформирован правильно (и clang правильно решил, что t2 является custom_tuple<int>).


Процесс выяснения того, что делать с

custom_tuple t2(42);

в основном включает в себя синтез набора функций и выполнение для них разрешения перегрузки. Подходящими кандидатами являются синтезированные функции из одного конструктора и руководства по выводу:

template <class... T, class... Args>
custom_tuple<T...> foo(Args... );     // the constructor

template <class... Args>
custom_tuple<Args...> foo(Args... );  // the deduction guide

С этого момента вы можете выбрать свое собственное приключение, основанное на вашей интерпретации того, что такое «конечный пакет параметров» в соответствии с [temp.arg.explicit]/3:

Замыкающий пакет параметров шаблона, не выведенный иным образом, будет преобразован в пустую последовательность аргументов шаблона. Если все аргументы шаблона могут быть выведены, все они могут быть опущены; в этом случае сам пустой список аргументов шаблона <> также может быть опущен.

T... не отстает

Этот случай легкий. У нас есть только один жизнеспособный кандидат (поскольку T... не выводим) — кандидат на дедукцию. Мы выводим Args... как {int}, так что в итоге получаем custom_tuple<int>.

T... отстает

И gcc, и clang фактически считают вывод конструктора успешным. Итак, мы переходим к разрешению конфликтов в [over.match.best]:

Учитывая эти определения, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если [...]

  • F1 и F2 являются специализациями шаблона функции, а шаблон функции для F1 является более специализированным, чем шаблон для F2 в соответствии с правилами частичного упорядочения, описанными в [temp.func.order], или, если не так,
  • F1 генерируется из руководства по дедукции ([over.match.class.deduct]), а F2 нет, или, если не так, [...]

В целях частичного упорядочения подходящие типы — это только те которые соответствуют параметрам функции, и нам разрешено игнорировать неиспользуемые параметры шаблона., поэтому ни один шаблон функции не считается более специализированным, чем другой.

Это оставляет нам возможность просто предпочесть руководство по дедукции, которое было самым простым шагом во всем этом процессе. Мы выводим Args... как {int}, так что в итоге получаем custom_tuple<int>.


В любом случае, custom_tuple<int> - правильное решение.

person Barry    schedule 03.06.2017
comment
Хм, ствол GCC и Clang расходится во мнениях по поводу кода OP, и я здесь не совсем уверен. Во-первых, T отстает? (Это довольно нечеткий термин.) Во-вторых, можете ли вы продемонстрировать, что конструктор является более специализированным, чем руководство по дедукции? - person T.C.; 03.06.2017
comment
@Т.С. Но здорово видеть, как Clang работает с проводниками по дедукции! Это должно быть совсем недавно. - person Barry; 03.06.2017
comment
Контекст здесь представляет собой вызов функции, поэтому используемые типы [для частичного упорядочения ] — это те типы параметров функции, для которых вызов функции имеет аргументы. А для целей частичного заказа параметр шаблона может оставаться без значения при условии, что оно не используется в типах, используемых для частичного упорядочения, иT... не используется. Так как именно один более специализирован, чем другой? В каком направлении дедукция терпит неудачу и как? - person T.C.; 03.06.2017
comment
@Т.С. Вы знаете, что забавно, я нашел другой ответ почти на тот же вопрос, где я (а) дал противоположный ответ и (б) сообщил об этой ошибке gcc. Кажется, я не могу держать этот материал прямо. - person Barry; 03.06.2017
comment
@Т.С. Переписал все. - person Barry; 03.06.2017