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
::typeor the_tsuffix 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_ofisn't a type trait so I don't know if I should be following that convention.I've made the template a
structbecause 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_builderis 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?
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 ifnis past the end of the tuple. \$\endgroup\$std::getworks withstd::arraytoo. 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\$