I'm trying to create a Tuple
class with an arbitrary number of entries with arbitrary types using variadic templates and the ability to get the n
th entry with a templatized entry
method, so I can use it as follows:
Tuple<int, int, std::string, double> t(1, 2, "Hello World", 3.4);
std::cout << t.entry<1>() << std::endl; // Prints 2
std::cout << t.entry<2>() << std::endl; // Prints "Hello World"
My current approach:
template<typename ...Types>
struct Tuple;
template<typename Type>
struct Tuple<Type>
{
Tuple(Type value) : value(value) { };
Type value;
template<int Index>
Type& entry()
{
return value;
}
};
template<typename Type, typename... Types>
struct Tuple<Type, Types...> : public Tuple<Types...>
{
Tuple(Type value, Types ...args) : Tuple<Types...>(args...), value(value) { }
Type value;
template<int Index>
auto entry() -> decltype(Tuple<Types...>::entry<Index-1>())&
{
return Tuple<Types...>::entry<Index-1>();
}
template<>
Type& entry<0>()
{
return value;
}
};
The first struct providing the "base" case for one element and the second struct building recursively upon that. However, I get the error
In member function ‘decltype (((Tuple<Types ...>::entry < (Index - 1)) > <expression error>))& Tuple<Type, Types ...>::entry()’:
error: expected primary-expression before ‘)’ token
How can I call a templated method of a templated base class?
Assuming you're using at least C++14, you can use auto&
instead of a trailing return type, and as pointed out by max66 you need to add the template
keyword before the member function invocation in your syntax.
You can also simplify your definition because you just need an empty base class, not a class which implements a specialization for one type; your second class already implements the necessary behavior for one type. This simplification requires you to rewrite entry()
using std::enable_if
, or if constexpr
if you're using C++17
// only needed for C++14 when using std::enable_if
#include <type_traits>
template<typename...>
struct Tuple
{
};
template<typename T, typename... Ts>
struct Tuple<T, Ts...> : public Tuple<Ts...>
{
Tuple(T value, Ts ...args) : value(value), Tuple<Ts...>(args...) { }
T value;
template<std::size_t I, std::enable_if_t<I == 0, bool> = true>
T& entry() { return value; }
template<std::size_t I, std::enable_if_t<I != 0, bool> = true>
auto& entry() { return Tuple<Ts...>::template entry<I - 1>(); }
// // requires C++17 support
// template<std::size_t I>
// auto& entry() {
// if constexpr (I == 0) { return value; }
// else { return Tuple<Ts...>::template entry<I - 1>(); }
// }
};
Try it on godbolt.org