Search code examples
c++boosttemplate-meta-programmingboost-mpl

How to create a boost::tuple with the specified number of elements (of the same type)?


Assume that I have the following class definition:

template <unsigned int N>
class foo
{
    boost::tuples::tuple<...> bar;
};

Given the compile-time constant N, I would like to expand the type of bar to be a tuple that holds N elements of a specified type. That is, the type of foo<2>::bar would be boost::tuples::tuple<T, T>. I'm guessing that I can use Boost.MPL for this, but I haven't figured out the exact sequence yet. I think I could do:

template <typename T, int N>
struct type_repeater
{
    typedef typename boost::mpl::fold<
        boost::mpl::range_c<T, 0, N>,
        boost::mpl::vector<>,
        boost::mpl::push_back<_1, T>
    >::type type;
};

So then for instance type_repeater<T, 2>::type would be equivalent to boost::mpl::vector<T, T>. I'm just not sure how/if I can take that type list and inject it into the argument list of a tuple, like I want. Is this possible?


Solution

  • Although this is totally doable with variadic templates and std::tuple, the best solution for what you want I think is to just use std::array. If you simply want a container with N instances of T, the std::array signature is already template <typename T, std::size_t N> class array. I think it fits your need exactly.

    Having said that, if you really want std::tuple for some reason you can do it like so:

    #include <tuple>
    
    /* Forward declaration. */
    template <std::size_t N, typename T>
    class Tuple;
    
    /* Convenience type alias. */
    template <std::size_t N, typename T>
    using TTuple = typename Tuple<N, T>::type;
    
    /* Base case. */
    template <typename T>
    class Tuple<0, T> {
      public:
    
      using type = std::tuple<>;
    
    }; // Tuple<0>
    
    /* Recursive case. */
    template <std::size_t N, typename T>
    class Tuple {
      public:
    
      /* Note the use of std::declval<> here. */
      using type = decltype(std::tuple_cat(std::declval<std::tuple<T>>(),
                                           std::declval<TTuple<N - 1, T>>()));
    
    };  // Tuple<N, T>
    
    /* std::declval<> is necessary to support non default constructable classes. */
    class NoDefault {
      public:
    
      NoDefault() = delete;
    
    };  // Foo
    
    /* Sample use. */
    static_assert(std::is_same<TTuple<2, NoDefault>,
                               std::tuple<NoDefault, NoDefault>>::value, "");
    
    int main() {}
    

    Note: If you don't have access to C++11 but do have access to boost, boost::array and boost::tuples::tuple will do fine in lieu of std::array and std::tuple.