Reputation: 2113
I'm trying to implement tuples with template metaprogramming, but am having problems with the indexing function get
. The implementation of Tuple
type is this:
template<typename A, typename... B>
class Tuple : Tuple<B...> {
private:
A val;
public:
using Base = Tuple<B...>;
Base* base() {
return static_cast<Base*>(this);
}
const Base* base() const {
return static_cast<const Base*>(this);
}
Tuple(A a, B... b): Base(b...), val(a) { }
A first() {
return val;
}
};
template<class A>
class Tuple<A> {
private:
A val;
public:
Tuple(A a): val{a} {}
A first() {
return val;
}
};
The implementation of get
structure is:
template<int N, class... A>
struct get {
select<N,A...> operator()(Tuple<A...> t) {
return get<N-1>()(t.base());
}
};
template<class R, class... A>
struct get<0> {
R operator()(Tuple<R, A...> t) {
return t.first();
}
};
This is the error compiler is giving me:
tuple.cpp:53:8: error: partial specialization of 'get' does not use any of its template parameters
struct get<0> {
^
1 error generated.
Why am I getting this error? How can I correct it?
Note: select<N,A...>
is a type function which selects type at Nth index from A
.
Upvotes: 3
Views: 1426
Reputation: 66190
Maybe you have to partial specialize get
as follows
template<class R, class... A>
struct get<0, R, A...> {
R operator()(Tuple<R, A...> t) {
return t.first();
}
};
I mean... get<0, R, A...>
, not get<0>
But you have also to modify the main get
to call the following call with the correct type list, so
template<int N, typename A0, typename ... As>
struct get {
auto operator()(Tuple<A0, As...> t) {
return get<N-1, As...>()(t.base());
}
};
Otherwise you can also demand the types management to a template version of the operator()
and maintain only the int N
value for get
template <int N>
struct get
{
template <typename Tpl>
auto operator() (Tpl t)
-> decltype( get<N-1>()(t.base()) )
{ return get<N-1>()(t.base()); }
};
template<>
struct get<0>
{
template <typename Tpl>
auto operator() (Tpl t)
-> decltype ( t.first() )
{ return t.first(); }
};
Starting from C++14 you can avoid the decltype()
part.
Off topic suggestion: avoid the use of names that can collide with std
namespace names.
Maybe myGet
and myTuple
instead of get
and Tuple
.
Otherwise you can put all in a personal namespace (so myNs::get
and myNs::Tuple
.
Upvotes: 1
Reputation: 24738
Your get
's primary template is:
template<int N, class... A>
struct get{ ... };
your get
's partial specialization is:
template<class R, class... A>
struct get<0>{ ... };
The specialization is receiving a single template argument, i.e.: 0
, but the primary template above takes two template parameters:
N
.A
.Besides, how can R
be deduced?
Specializing get
instead as:
template<class R, class... A>
struct get<0, R, A...>{ ... };
will make possible R
to be deduced: it will be deduced to the type of the first element of the passed variadic argument. For example, in:
get<0, int, float, double> foo;
R
will be deduced to int
.
Upvotes: 1