Variadic Template Template Argument Instance

I want to create an instance of the variation template class Store<TArgs...> that has std::vector for each type in the TArgs... package TArgs...

 template<typename... TArgs> class Store { // obviously not valid code // assuming each type of TArgs... has a `unsigned int` id that can be // retrieved with getId<T>() std::array<sizeof...(TArgs), std::vector<TArgs...>> bags; template<typename T> void add(T mValue) { bags[getId<T>()].push_back(mValue); } template<typename T> std::vector<T>& get() { return bags[getId<T>()]; } }; 

Let's say I have Store<int, float, double> . I obviously know, at compile time, that it will be able to store int , float and double values.

I could use specialized templates:

 template<> class Store<int, float, double> { std::vector<int> vi; std::vector<float> vf; std::vector<double> vd; template<typename T> void add(T); template<> void add<int>(int mValue) { vi.push_back(mValue); } template<> void add<float>(float mValue) { vf.push_back(mValue); } template<> void add<double>(double mValue) { vd.push_back(mValue); } // ... }; 

... but this will require manual spelling of every possible combination of types and will not work with user-defined types.

I am sure that the compiler knows everything that is required to create a class like Store<int, float, double> using variable templates - is there any way to express this intention?

+8
c ++ c ++ 11 templates variadic-templates
source share
2 answers

The following should do what you want:

 #include <type_traits> #include <vector> #include <tuple> #include <iostream> // indices are a classic template< std::size_t... Ns > struct indices { using next = indices< Ns..., sizeof...( Ns ) >; }; template< std::size_t N > struct make_indices { using type = typename make_indices< N - 1 >::type::next; }; template<> struct make_indices< 0 > { using type = indices<>; }; // we need something to find a type index within a list of types template<typename T, typename U, std::size_t=0> struct index {}; template<typename T, typename... Us, std::size_t N> struct index<T,std::tuple<T,Us...>,N> : std::integral_constant<std::size_t, N> {}; template<typename T, typename U, typename... Us, std::size_t N> struct index<T,std::tuple<U,Us...>,N> : index<T,std::tuple<Us...>,N+1> {}; // we need a way to remove duplicate types from a list of types template<typename T,typename I=void> struct unique; // step 1: generate indices template<typename... Ts> struct unique< std::tuple<Ts...>, void > : unique< std::tuple<Ts...>, typename make_indices<sizeof...(Ts)>::type > { }; // step 2: remove duplicates. Note: No recursion here! template<typename... Ts, std::size_t... Is> struct unique< std::tuple<Ts...>, indices<Is...> > { using type = decltype( std::tuple_cat( std::declval< typename std::conditional<index<Ts,std::tuple<Ts...>>::value==Is,std::tuple<Ts>,std::tuple<>>::type >()... ) ); }; // a helper to turn Ts... into std::vector<Ts>... template<typename> struct vectorize; template<typename... Ts> struct vectorize<std::tuple<Ts...>> { using type = std::tuple< std::vector<Ts>... >; }; // now you can easily use it to define your Store template<typename... Ts> class Store { using Storage = typename vectorize<typename unique<std::tuple<Ts...>>::type>::type; Storage storage; template<typename T> decltype(std::get<index<T,typename unique<std::tuple<Ts...>>::type>::value>(storage)) slot() { return std::get<index<T,typename unique<std::tuple<Ts...>>::type>::value>(storage); } public: template<typename T> void add(T mValue) { slot<T>().push_back(mValue); } template<typename T> std::vector<T>& get() { return slot<T>(); } }; int main() { Store<int,int,double,int,double> store; store.add(42); store.add(3.1415); store.add(21); std::cout << store.get<int>().size() << std::endl; std::cout << store.get<double>().size() << std::endl; } 

Live example (no comment)

+9
source share

In C ++ 14, std::tuple elements are accessible by type if there are no multiple elements of the same type. Therefore, you should write it as follows:

 template<typename... TArgs> struct Store { std::tuple<std::vector<TArgs>...> bags; template<typename T> void add(T mValue) { get<T>().push_back(mValue); } template<typename T> std::vector<T>& get() { return std::get<std::vector<T>>(bags); } }; 
+5
source share

All Articles