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

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

#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...) {}

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

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> это правильное решение.

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