Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Produce std::tuple of same type in compile time given its length by a template argument

In c++, how can I implement a function with an int template argument indicating the tuple length and produce a std::tuple with that length?

E.g.

func<2>() returns std::tuple<int, int>();
func<5>() returns std::tuple<int, int, int, int, int>().
like image 602
yuefengz Avatar asked Aug 11 '16 00:08

yuefengz


3 Answers

Here is a recursive solution with alias template and it's implementable in C++11:

template <size_t I,typename T> 
struct tuple_n{
    template< typename...Args> using type = typename tuple_n<I-1, T>::template type<T, Args...>;
};

template <typename T> 
struct tuple_n<0, T> {
    template<typename...Args> using type = std::tuple<Args...>;   
};
template <size_t I,typename T>  using tuple_of = typename tuple_n<I,T>::template type<>;

For example if we want "tuple of 3 doubles" we can write:

tuple_of<3, double> t;
like image 152
rahnema1 Avatar answered Nov 01 '22 13:11

rahnema1


Using an index_sequence and a helper type alias you can generate the type you want:

// Just something to take a size_t and give the type `int`
template <std::size_t>
using Integer = int;

// will get a sequence of Is = 0, 1, ..., N
template <std::size_t... Is>
auto func_impl(std::index_sequence<Is...>) {
    // Integer<Is>... becomes one `int` for each element in Is...
    return std::tuple<Integer<Is>...>{};
}

template <std::size_t N>
auto func() {
    return func_impl(std::make_index_sequence<N>{});
}

It is worth calling out that in the general case you would probably be better with a std::array, (in your case you can't use one), but a std::array can behave like a tuple, similarly to a std::pair.

Update: since you've made it clear you're working with c++11 and not 14+, you'll need to get an implementation of index_sequence and related from somewhere (here is libc++'s). Here is the C++11 version of func and func_impl with explicit return types:

template <std::size_t... Is>
auto func_impl(std::index_sequence<Is...>) -> std::tuple<Integer<Is>...> {
  return std::tuple<Integer<Is>...>{};
}

template <std::size_t N>
auto func() -> decltype(func_impl(std::make_index_sequence<N>{})) {
  return func_impl(std::make_index_sequence<N>{});
}
like image 9
Ryan Haining Avatar answered Nov 01 '22 13:11

Ryan Haining


The plain old recursion is your friend:

template<std::size_t N>
auto array_tuple() {
    return std::tuple_cat(std::tuple<int>{}, array_tuple<N-1>());
}

template<>
auto array_tuple<0>() {
    return std::tuple<>{};
}
like image 5
eerorika Avatar answered Nov 01 '22 11:11

eerorika