C++11 provides a std::tuple
template, but it requires the types of all the fields to be listed individually, which is inconvenient if there are many fields and they're all the same type. If I want a tuple of, say, 10 integers, instead of writing std::tuple<int, int, int, int, int, int, int, int, int, int>
, I'd like to be able to just write something like tuple_of<10, int>
.
To that end, I've written a template that expands a number into that many copies of a type:
template <unsigned int n, typename T>
struct tuple_of {
private:
// Adds another copy of T to the list
template <unsigned int m, typename... Ts>
struct tuple_builder {
using type = typename tuple_builder<m-1, T, Ts...>::type;
};
// Makes a tuple out of all the copies of T that have been listed
template <typename... Ts>
struct tuple_builder<0, Ts...> {
using type = std::tuple<Ts...>;
};
public:
tuple_of() = delete; // Prevent accidental instantiation
using type = typename tuple_builder<n, T>::type;
};
// Convenience alias
template <unsigned int n, typename T>
using tuple_of_t = typename tuple_of<n, T>::type;
Now you can write either tuple_of<10, int>::type
or tuple_of_t<10, int>
.
This works, but it seems a little awkward. A few things in particular bother me:
I don't really like having to write either
::type
or the_t
suffix of the convenience alias; I'd rather just writetuple_of<10, int>
. I've followed the pattern established by the templates in the standard<type_traits>
header — e.g.std::result_of_t<T>
is an alias forstd::result_of<T>::type
— buttuple_of
isn't a type trait so I don't know if I should be following that convention.I've made the template a
struct
because that seems to be the norm for templates used in metaprogramming, but it contains a private helper template that I don't really want to expose, and it's a little weird to have private stuff in astruct
.This is the first variadic template I've written, so I'm not sure that my
tuple_builder
is the most elegant way to build a list of n copies of a type.While testing, I accidentally created an instance of the template type itself, rather than the tuple type it produces, by writing
tuple_of<10, int>
instead oftuple_of<10, int>::type
. To avoid that mistake in the future, I deleted the constructor. I don't know whether that's something templates like this "should" do; the templates in<type_traits>
don't have deleted constructors.
Can this template be improved?
1 Answer 1
As nwp pointed out in a comment, std::array
is a better choice for this. I didn't need to write this template at all.
Explore related questions
See similar questions with these tags.
std::array<int, 10>
? \$\endgroup\$std::array
, it looks likeoperator[]
doesn't prevent out-of-bounds access, andat()
checks at runtime. Usingstd::get<n>
on a tuple will fail to compile ifn
is past the end of the tuple. \$\endgroup\$std::get
works withstd::array
too. And the page on cppreference.com even mentions that "an array can also be used as a tuple of N elements of the same type." \$\endgroup\$