We have a constructible **tuple** at this point, but we don’t have a way of getting tuple elements by index or in any other way. It’s time to implement the main accessor — **get<>** — which we use to read and write the tuple’s elements. There are two flavors of the **get<>** template: *get by index* and *get by type*, the latter being part of **tuple**‘s interface since C++ 14. We are going to implement the latter in terms of the former.

Here are the overloads we need for **get<>**:

template <size_t I, typename... Types> ??? get(tuple<Types...> const& tup); template <size_t I, typename... Types> ??? get(tuple<Types...>& tup); template <size_t I, typename... Types> ??? get(tuple<Types...>&& tup);

It’s pretty clear that the return types and contents of all three functions will be very similar. Let’s start with the return type — what should it be?

**Exercise 9**: Implement a metafunction **type_at_index<I, Types…>** which returns the type of the **I**‘th element in the type parameter pack **…Types**.

This is yet another boring variadic template recursion:

template <size_t I, typename Head, typename... Tail> struct type_at_index { using type = typename type_at_index<I-1, Tail...>::type; }; template <typename Head, typename... Tail> struct type_at_index<0, Head, Tail...> { using type = Head; }; template <size_t I, typename... Types> using type_at_index_t = typename type_at_index<I, Types...>::type;

Great. We can now fill in the return types:

template <size_t I, typename... Types> type_at_index_t<I, Types...> const& get(tuple<Types...> const& tup); template <size_t I, typename... Types> type_at_index_t<I, Types...>& get(tuple<Types...>& tup); template <size_t I, typename... Types> type_at_index_t<I, Types...>&& get(tuple<Types...>&& tup);

**Exercise 10**: The third declaration is wrong. What’s wrong with it?

Well, if **type_at_index** returns an lvalue reference type, then the return type is going to be an lvalue reference as well because of the reference collapsing rules (**& &&** becomes **&**)! For example:

int x = 42; tuple<int&> t(x); get<0>(std::move(t)); // returns int&!

This is easily fixable — we just need to remove the reference qualifier:

template <size_t I, typename... Types> std::remove_reference_t<type_at_index_t<I, Types...>>&& get(tuple<Types...>&& tup);

OK, now how about the bodies of these methods? We designed **tuple** from the outset to make the implementation easy. Our **tuple_element**s are already indexed, so we just need to cast the **tuple** instance to the appropriate base class, and extract the **value_** field. For example:

template <size_t I, typename... Types> type_at_index<I, Types...>& get(tuple<Types...>& tup) { tuple_element<I, type_at_index_t<I, Types...>>& base = tup; return base.value_; }

Awesome. The other two methods are obviously similar. Now let’s take a look at the other kind of get — **get<T>**. It needs to verify that the type exists exactly once in the **tuple**, and return the element of that type.

tuple<int, string> t1; get<int>(t1) = 42; get<float>(t1) = 3.14f; // compilation error tuple<int, int> t2; get<int>(t2) = 42; // compilation error

Our basic approach will be as follows: count the number of times **T** appears in the tuple. If it is not exactly 1, bail with a compilation error. Otherwise, find the index **I** at which **T** appears and call **get<I>***. *And again we’re going to use **constexpr** functions for most of the work — it’s so much easier and more elegant than using structs:

template <typename> constexpr int count() { return 0; } template <typename T, typename Head, typename... Tail> constexpr int count() { return (std::is_same<T, Head>::value ? 1 : 0) + count<T, Tail...>(); } template <typename> constexpr int find(int) { return -1; } template <typename T, typename Head, typename... Tail> constexpr int find(int current_index = 0) { return std::is_same<T, Head>::value ? current_index : find<T, Tail...>(current_index + 1); } template <typename T, typename... Types> T& get(tuple<Types...>& tup) { static_assert(count<T, Types...>() == 1, "T must appear exactly once in ...Types"); get<find<T, Types...>()>(tup); }

Hopefully by now you’re starting to appreciate how beautiful, elegant, and concise tuples can be. They really bring the best out of metaprogramming and give us the opportunity to experiment with a bunch of various techniques.

As homework, you can implement some additional functionality on our **tuple** class, which I am too lazy to do here. For example:

* Copy and move assignment operators from tuple

* Comparison operators: ==, !=, <, <=, >, >=

* Tuple construction from **std::pair** and **std::array**

In our next installment we’re going to deal with a couple of simple non-member functions before moving on to **tuple_cat**, which will be the crown jewel of this series.

Nice article, thanks. But I think

template

type_at_index

& get(tuple& tup)

{

...

}

should be

template

typename type_at_index

::type& get(tuple& tup){

...