我想实例化一个变量模板类Store<TArgs...>
,std::vector
包中的每种类型都有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>()];
}
};
假设我有Store<int, float, double>
。我很清楚,在编译时,它可以存储int
,float
和double
值。
我可以使用模板专业化:
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 :(得分:10)
以下应该做你想做的事:
#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(没有评论)
答案 1 :(得分:6)
在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);
}
};