Руководство по дедукции и вариационные шаблоны
Рассмотрим следующий код:
#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>
это правильное решение.