Search code examples
c++templatesvariadic-templates

Best way to generate a variadic argument list containing N arguments of a given type?


There is a class template of the form:

template <typename ...T> Bag {};

with lots of useful functionality, but some implementation left to derived classes. And often this is instantiated with a repetition of one type N times. Instead of defining classes like:

class BoolBag3 : public Bag<bool, bool, bool> {};
class IntBag2 : public Bag<int, int> {};

I am interested in having a class like:

template<size_t N, typename T> class UniBag : public Bag< ... > {}

especially since some pure virtuals in Bag can be implemented in a generic way when all types are the same. What is the simplest / most elegant way to do this?

What I have done so far: Use the pattern in this answer to generate a TypeSequence:

template<class T> using InvokeType = typename T::type;  // InvokeType is an alias for T::type.
template<class S1, class S2> struct concat;

// Type sequence.

template<typename ... T> struct TypeSequence{ using type = TypeSequence; };

template<typename ... T, typename ... U> 
struct concat<TypeSequence<T ...>, TypeSequence<U ...>> : TypeSequence<T ..., U ...>{};
template<class S1, class S2> using Concat = InvokeType<concat<S1, S2>>;

template<typename T, size_t N> struct gen_type_seq;
template<typename T, size_t N> using GenTypeSeq = InvokeType<gen_type_seq<T, N>>;

template<typename T, size_t N> 
struct gen_type_seq : Concat<GenTypeSeq<T, N/2>, GenTypeSeq<T, N - N/2>>{};

template<typename T> struct gen_type_seq<T, 0> : TypeSequence<>{};
template<typename T> struct gen_type_seq<T, 1> : TypeSequence<T>{};

Then use an intermediate class to convert TypeSequence to the variadic argument list:

template <typename T> class UniBagHelper {};
template <typename ... T> class UniBagHelper<TypeSequence<T ...>> : public Bag<T ...> {};

template <typename T, size_t N> class UniBag : public UniBagHelper<GenTypeSeq<T, N>> {};

UniBag<bool, 4> logicBag;

Solution

  • Something along these lines, perhaps:

    template <typename T, size_t dummy>
    using EatIndex = T;
    
    template <typename T, std::size_t... I>
    Bag<EatIndex<T, I>...> MakeBagN(std::index_sequence<I...>);
    
    template<size_t N, typename T>
    using BagN = decltype(MakeBagN<T>(std::make_index_sequence<N>()));
    
    template<size_t N, typename T>
    class UniBag : public BagN<N, T> {};
    

    Demo