2013-10-19 37 views
8

我想實例化一個可變參數模板類Store<TArgs...>,它對於TArgs...包中的每種類型都有一個std::vector可變參數模板類參數容器實例

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

比方說,我有一個Store<int, float, double>。我在編譯時顯然知道它將能夠存儲int,floatdouble值。

我可以使用模板特:

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

...但這需要手寫的類型每個可能的組合,並且不會與用戶定義類型的工作。

我相信編譯器知道使用可變參數模板生成像Store<int, float, double>這樣的類所需的一切 - 有沒有一種方法可以真正表達這種意圖?

+0

你需要一個'std :: tuple'來做到這一點,'std :: vector'不適合。 –

+0

這裏>> std :: array >',模板參數的順序是錯誤的。它應該是'std :: array ',而不是'std :: array '。另外,我認爲你需要'std :: tuple',而不是'std :: vector'(我不確定)。 – Nawaz

回答

9

以下應該做你想要什麼:

#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(沒有評論)

+0

@VittorioRomeo重讀你的問題,上面的可能會比你所要求的更多:*它只處理一個向量,因此'Store '的行爲與'Store '。但你所要求的所有情況也會自動涵蓋:) –

+0

非常感謝。聰明有效的解決方案! –

5

在C++ 14 std::tuple元素是按類型提供的訪問不會有幾個要素相同的類型。因此,你應該可以這樣寫:

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

看起來不錯,不能等待C++ 14 :) –