Во-первых, до CWG 727 вам не разрешается специализировать шаблон функции-члена внутри области класса. Вам нужно будет использовать constexpr-if
, tag-dispatching или SFINAE для обработки случая i==0
.
В c ++ 14 , используя std::enable_if_t
, это будет:
template <int i, typename T>
std::enable_if_t<i != 0> Set(T&& v)
{
tail.template Set<i-1>(static_cast<T&&>(v));
}
template <int i, typename T>
std::enable_if_t<i == 0> Set(T&& v)
{
head = static_cast<T&&>(v);
}
В c ++ 17 с использованием constexpr-if
это становится:
template <int i, typename T>
void Set(T&& v)
{
if constexpr (i == 0) head = static_cast<T&&>(v);
else tail.template Set<i-1>(static_cast<T&&>(v));
}
Во-вторых, как только компиляторы позволяют вам специализировать шаблон функции внутри класса справиться, есть еще одна проблема с вашим текущим подходом. Ваша реализация MakeTuple
, из-за того, как вывод аргументов шаблона работает для пересылки ссылок, создает кортеж ссылочных типов, соответствующих тем MakeTuple
аргументам, которые являются lvalue:
template <typename Head, typename... Tail>
Tuple<Head, Tail...> MakeTuple(Head&& head, Tail&&... tail);
Это делает ваш комментарий / предположение:
void Set<0, Head>(Head&& v) // Head&& is an rv-ref
недействительно.
То есть для выражения lvalue s
:
S s{ 4, 5 };
MakeTuple(s);
выведенное Head
равно S&
(это также тип head
после свертывания ссылки). Затем компилятор пытается создать экземпляр Tuple<S&>
, и он генерирует следующие два объявления:
void Set<0, S&>(S& && v);
void Set<0, S&>(S& const& v);
После сворачивания ссылки он заканчивается с:
void Set<0, S&>(S& v);
void Set<0, S&>(S& v);
На этом этапе не только оба определения те же, но также компилятор не может решить, какие из основных шаблонов функций:
template <int i, typename T>
void Set(T&& v);
template <int i, typename T>
void Set(const T& v);
они являются специализациями, поскольку использование T=S&
соответствует обоим. Эту проблему можно решить, разрушив каждый тип перед сохранением его в кортеж:
template <typename Head, typename... Tail>
Tuple<std::decay_t<Head>, std::decay_t<Tail>...> MakeTuple(Head&& head, Tail&&... tail);