Use std::tuple for template parameter list instead of list of types

c++, enable-if, template-templates, tuples, variadic-templates

Solution

Your first solution is failing because the second overload to `get` is not visible at the point of its own return type declaration; to get around this you would need to separate out the return type computation into its own subprogram.

The second solution is closer; the problem is that you're only inferring the template `std::tuple`, not its arguments. An easy way to infer variadic arguments (e.g. type arguments to `tuple`) is through an empty tag structure, requiring one extra level of indirection:

template<typename T> struct type_tag {};

class Cache {
    // ... (as above)

    template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) {
        return get<0, Ts...>();
    }

public:
    template<typename T> T get() {
        return get(type_tag<T>{});
    }
};

You should check to see whether you can write the solution using pack expansion instead of recursion, for example:

template<typename T> struct type_tag {};

class Cache {
    template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) {
        return std::tuple<Ts...>{Ts{}...};
    }

public:
    template<typename T> T get() {
        return get(type_tag<T>{});
    }
};

Problem

I'm trying to make a call to a templated function like this : ``` typedef std::tuple<int, double, bool> InstrumentTuple; Cache cache; InstrumentTuple tuple = cache.get<InstrumentTuple>(); ``` I know I could "simply" pass the types of the tuple. This is what I do know but it is quite cumbersome since I make a lot of calls to this function and since the tuples are quite long: ``` InstrumentTuple tuple = c.get<int, double, bool>(); // syntax I'd like to avoid ``` So I tried multiple implementations of the get method, but with no success : Enabling via a template parameter ``` #include <tuple> class Cache { private: template<int I, typename T, typename = typename std::enable_if<I == std::tuple_size<T>::value>::type> std::tuple<> get() // line 6 { return std::tuple<>(); } template<int I, typename T, typename = typename std::enable_if<I != std::tuple_size<T>::value>::type> std::tuple<typename std::tuple_element<I,T>::type, decltype(get<I+1, T>())> get() // line 12 { std::tuple<typename std::tuple_element<I,T>::type> value; return std::tuple_cat(value, get<I+1, T>()); } public: template<typename T> T get() { return get<0, T>(); // line 22 } }; int main(int argc, char** argv) { Cache cache; typedef std::tuple<int, double, bool> InstrumentTuple; InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30 } ``` Which gives me this error : ``` main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]': main.cpp:30:56: required from here main.cpp:22:26: error: no matching function for call to 'Cache::get()' main.cpp:22:26: note: candidates are: main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get() main.cpp:6:18: note: template argument deduction/substitution failed: main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>' main.cpp:12:81: note: template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get() // ----- Important part main.cpp:12:81: note: template argument deduction/substitution failed: main.cpp: In substitution of 'template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get() [with int I = 0; T = std::tuple<int, double, bool>; <template-parameter-1-3> = <missing>]': // ----- main.cpp:22:26: required from 'T Cache::get() [with T = std::tuple<int, double, bool>]' main.cpp:30:56: required from here main.cpp:12:81: error: no matching function for call to 'Cache::get()' main.cpp:12:81: note: candidate is: main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get() main.cpp:6:18: note: template argument deduction/substitution failed: main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>' main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]': main.cpp:30:56: required from here main.cpp:20:7: note: template<class T> T Cache::get() main.cpp:20:7: note: template argument deduction/substitution failed: main.cpp:22:26: error: wrong number of template arguments (2, should be 1) ``` I don't get why is there a missing template parameter. So I tried another implementation : Template-template named parameter ``` #include <tuple> class Cache { private: template<int> std::tuple<> get() // line 7 { return std::tuple<>(); } template<int index, typename type, typename... rest> std::tuple<type, rest...> get() // line 13 { return std::tuple_cat(std::tuple<type>(), get<index+1, rest...>()); } public: template<template<typename... types> class tuple> typename std::tuple<(tuple::types)...> get() { return get<0, (tuple::types)...>(); } }; // line 24 int main(int argc, char** argv) { Cache cache; typedef std::tuple<int, double, bool> InstrumentTuple; InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30 } ``` But then I get this error : ``` // ----- Important part main.cpp:24:1: error: expected identifier before '}' token main.cpp:24:1: error: expected unqualified-id before '}' token // ----- main.cpp: In function 'int main(int, char**)': main.cpp:30:56: error: no matching function for call to 'Cache::get()' main.cpp:30:56: note: candidates are: main.cpp:7:18: note: template<int <anonymous> > std::tuple<> Cache::get() main.cpp:7:18: note: template argument deduction/substitution failed: main.cpp:13:31: note: template<int index, class type, class ... rest> std::tuple<_Head, _Tail ...> Cache::get() main.cpp:13:31: note: template argument deduction/substitution failed: ``` Again, I don't understand the errors due to a missing identifier. I'm wondering now if what I want to achieve is even possible. Is it possible to use a `std::tuple` like I want to ? Or is there a better way ?

Original source

Related problems