2013-10-19 21 views
8

Voglio istanziare una classe modello variadica Store<TArgs...> che ha un per ogni tipo nel pacchetto TArgs....Variabile modello classe argomento istanza contenitori

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>()]; 
    } 
}; 

Diciamo che ho un Store<int, float, double>. Ovviamente, al momento della compilazione, so che sarà in grado di memorizzare i valori int, float e double.

ho potuto utilizzare specializzazioni modello:

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); } 
    // ... 
}; 

... ma che richiederebbe ogni possibile combinazione di tipi di scrittura a mano, e non avrebbe funzionato con i tipi definiti dall'utente.

Sono sicuro che il compilatore conosce tutto ciò che è necessario per generare una classe come Store<int, float, double> utilizzando modelli variadic - esiste un modo per esprimere effettivamente questo intento?

+0

Avrai bisogno di una 'std :: tuple' per fare ciò,' std :: vector' non è adatto. –

+0

Qui >> 'std :: array >', l'ordine degli argomenti del modello è errato. Dovrebbe essere 'std :: array ', non 'std :: array '. Inoltre, penso che tu abbia bisogno di 'std :: tuple', non' std :: vector' (non sono sicuro però). – Nawaz

risposta

9

Il seguente dovrebbe fare quello che vuoi:

#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's 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 (senza commenti)

+0

@VittorioRomeo Rileggendo la tua domanda, quanto sopra probabilmente * più * di quanto hai chiesto: Gestisce i tipi duplicati fornendo solo un vettore, quindi 'Store ' si comporta allo stesso modo di 'Store '. Ma anche tutti i casi che hai richiesto sono automaticamente coperti :) –

+0

Grazie mille. Soluzione intelligente ed efficace! –

5

In C++ 14, gli elementi di std::tuple sono accessibili per tipo purché non ci sono diversi elementi di lo stesso tipo Pertanto, dovresti essere in grado di scrivere in questo modo:

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); 
    } 
}; 
+0

Sembra fantastico, non vedo l'ora che arrivi C++ 14 :) –

Problemi correlati