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>().
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>().
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;
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>{});
}
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<>{};
}
If you're okay with a C++14 solution, Ryan's answer is the way to go.
With C++11, you can do the following (still based on index_sequence
, but that's implementable in C++11):
template <size_t N, class T, class = std::make_index_sequence<N>>
struct n_tuple;
template <size_t N, class T, size_t... Is>
struct n_tuple<N, T, std::index_sequence<Is...>> {
template <size_t >
using ignore = T;
using type = std::tuple<ignore<Is>...>;
};
template <size_t N, class T>
using n_tuple_t = typename n_tuple<N, T>::type;
With that:
template <size_t N>
n_tuple_t<N, int> func() {
return n_tuple_t<N, int>{};
}
Here are two boost.hana solutions (C++14):
//first
hana::replicate<hana::tuple_tag>(int{}, hana::size_c<2>);
//second
hana::cycle(std::make_tuple(int{}), hana::size_c<2>);
Both produce integer-tuples of size 2, but instead of std::tuple
s they yield hana::tuple
s.