Если вы хотите явно специализировать функцию (функцию, шаблон функции члена, ..), то вы должны сделать это в области видимости пространства имен:
template <typename T1, typename ... tail>
class record : public record<tail...>
{
using baseT = record<tail...>;
T1 elem;
public:
record(T1 first, tail... rest) // you should use perfect forwarding here
: elem(first), baseT(rest...)
{}
template <int index>
inline constexpr T1& get() // the `inline` is redundant here
{
// line 83:
return baseT::get<index-1>();
}
};
template<typename T1, typename ... tail>
template<>
inline constexpr T1& record<T1, tail...>::template get<0>()
{ return elem; }
Но это не допускается: Вы не может явно специфицировать член не явно специализированного шаблона класса. Здесь record<T1, tail...>
не является явно специализированным; поэтому вы не можете явно специализировать get
.
Есть две другие проблемы:
- Тип возврата
get
должен зависеть от индекса.
- Ваш реестр будет рекурсивно выводиться из самого себя. Каждый вывод удаляет один элемент из
tail
, поэтому tail
заканчивается пустым. Тогда record<tail...>
не сработает, поскольку первый параметр шаблона T1
не может быть установлен, когда tail
пуст. Поэтому вам необходимо также специализироваться на record
.
Один из способов заставить его работать, чтобы использовать перегрузки:
#include <type_traits>
#include <utility>
template<int N>
using int_const = std::integral_constant<int, N>;
template <typename T1, typename ... tail>
class record : public record<tail...>
{
using baseT = record<tail...>;
T1 elem;
protected:
using baseT::get_impl; // "unhide" the base class overloads
constexpr T1 const& get_impl(int_const<sizeof...(tail)>) const
{
return elem;
}
public:
template<typename T1_, typename ... tail_>
record(T1_&& first, tail_&&... rest)
: baseT(std::forward<tail_>(rest)...), elem(std::forward<T1_>(first))
{}
template <int index>
constexpr auto get() const
-> decltype(this->get_impl(int_const<sizeof...(tail) - index>{}))
{
static_assert(1+sizeof...(tail) > index, "out of bounds");
return this->get_impl(int_const<sizeof...(tail) - index>{});
}
};
template <typename T1>
class record<T1>
{
T1 elem;
protected:
constexpr T1 const& get_impl(int_const<0>) const
{
return elem;
}
public:
template<typename T1_>
record(T1_&& first)
: elem(first)
{}
template <int index>
constexpr auto get() const
-> decltype(get_impl(int_const<index>{}))
{
static_assert(0 == index, "out of bounds");
return this->get_impl(int_const<index>{});
}
};
#include <iostream>
int main()
{
record<int, double, char, bool> r{42, 1.2, 'c', false};
std::cout << r.get<1>() << '\n';
std::cout << r.get<0>() << '\n';
}
Вот пример, используя другой метод наследования:
#include <type_traits>
#include <utility>
template<int N>
using int_const = std::integral_constant<int, N>;
template<int N, class... Ts>
struct record_impl
{
struct out_of_bounds {};
template<int I>
constexpr out_of_bounds get(int_const<I>) const
{
static_assert(I < N, "out of bounds");
return {};
}
};
template<int N, class T, class... Ts>
struct record_impl<N, T, Ts...> : record_impl<N+1, Ts...>
{
using base = record_impl<N+1, Ts...>;
T mem;
template<class Arg, class... Args>
record_impl(Arg&& arg, Args&&... args)
: base(std::forward<Args>(args)...), mem(std::forward<Arg>(arg))
{}
using base::get;
constexpr T const& get(int_const<N>) const
{ return mem; }
};
template<class... Ts>
using record = record_impl<0, Ts...>;
#include <iostream>
int main()
{
record<int, double, char, bool> r{42, 1.2, 'c', false};
std::cout << r.get(int_const<0>{}) << '\n';
std::cout << r.get(int_const<3>{}) << '\n';
}
Использование record_impl
позволяет получить избавиться от дополнительных get_impl
. Он также предоставляет хорошую возможность разместить static_assert
в функции члена основного элемента шаблона get
.
:/static assert не поможет, так как тип возвращаемого значения не может быть определен в 'decltype', если индекс не соответствует границам. Возможно, вам понадобится сделать более сложную вещь, чтобы вызвать 'static_assert' в случае доступа вне границ. – dyp
Btw: все классы в иерархии будут иметь функцию 'get', но они скрыты высшим классом' get', который имеет правильный 'sizeof ... (tail)'. Этот 'sizeof' необходим, поскольку только верхний класс знает истинную длину хвоста, но элементы в хвосте назначаются в обратном порядке на уровень класса в иерархии. – dyp
Примечание. Вы можете использовать элемент вместо наследования (это также решит обратный порядок и скрытый метод). – Jarod42