6
\$\begingroup\$

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 write tuple_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 for std::result_of<T>::type — but tuple_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 a struct.

  • 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 of tuple_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?

\$\endgroup\$
  • 6
    \$\begingroup\$ What is wrong with std::array<int, 10>? \$\endgroup\$ – nwp Jan 4 '15 at 23:46
  • \$\begingroup\$ That could probably work, but I'd like to have compile-time checking that I'm only accessing valid elements. With std::array, it looks like operator[] doesn't prevent out-of-bounds access, and at() checks at runtime. Using std::get<n> on a tuple will fail to compile if n is past the end of the tuple. \$\endgroup\$ – Wyzard Jan 4 '15 at 23:50
  • 1
    \$\begingroup\$ Ahh, nevermind, I see that std::get works with std::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\$ – Wyzard Jan 5 '15 at 0:33
  • \$\begingroup\$ If the types are all the same. Then we are talking about an array. \$\endgroup\$ – Martin York Jan 5 '15 at 1:54
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.