【问题标题】:Variadic template class argument containers instantiation可变模板类参数容器实例化
【发布时间】:2013-10-28 03:02:09
【问题描述】:

我想实例化一个可变参数模板类 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&lt;int, float, double&gt;。我显然知道,在编译时,它将能够存储 intfloatdouble 值。

我可以使用模板特化:

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&lt;int, float, double&gt; 这样的类所需的一切 - 有没有办法真正表达这种意图?

【问题讨论】:

  • 您需要std::tuple 来执行此操作,std::vector 不适合。
  • 这里>>std::array&lt;sizeof...(TArgs), std::vector&lt;TArgs...&gt;&gt;,模板参数的顺序错误。它应该是std::array&lt;Type, Size&gt;,而不是std::array&lt;Size, Type&gt;。另外,我认为你需要std::tuple,而不是std::vector(虽然我不确定)。

标签: c++ templates c++11 variadic-templates


【解决方案1】:

在 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); 
    }
};

【讨论】:

  • 看起来不错,等不及 C++14 :)
【解决方案2】:

以下应该做你想要的:

#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(没有 cmets)

【讨论】:

  • @VittorioRomeo 重读您的问题,上面的内容可能比您要求的更多:它通过仅提供一个向量来处理重复类型,因此Store&lt;int,double&gt; 的行为与@ 相同987654324@。但是您要求的所有案例也会自动涵盖:)
  • 非常感谢。聪明有效的解决方案!
猜你喜欢
  • 2014-09-13
  • 2014-06-30
  • 2015-06-08
  • 2014-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多