Implementing std::tuple from the Ground Up – Part 5: Tuple Non-Member Functions

February 3, 2015

tags: ,
one comment

After doing all the heavy lifting in the previous four installments ([1], [2], [3], [4]), this one is going to be very lightweight. We will implement a few simple tuple non-member functions and helpers.

First, let’s implement a very simple helper: tuple_size. It is a metafunction that returns the number of elements in the tuple.

Exercise 11: Implement tuple_size.

Solution:

template <typename>
struct tuple_size; // undefined base template

template <typename... Types>
struct tuple_size<tuple<Types...>> : std::integral_constant<size_t, sizeof...(Types)>
{
};

Well, that was easy. Let’s do another: forward_as_tuple. The basic idea is that you provide a set of elements and get back a tuple that has lvalue or rvalue references depending on the original value categories. For example:

string s, t;
auto t1 = forward_as_tuple(s, t);       // t1 is tuple<string&, string&>
auto t2 = forward_as_tuple(move(s), t); // t2 is tuple<string&&, string&>

Exercise 12: Implement forward_as_tuple.

Solution:

template <typename... Types> 
tuple<Types&&...> forward_as_tuple(Types&&... elements)
{
  return tuple<Types&&...>(std::forward<Types>(elements)...);
}

Next, we’re going to implement the tie function. It is a pretty useful helper that can bind a tuple to individual variables. Here are a couple of examples (the second example assumes that our tuple has an assignment operator that accepts std::pair):

int status_code; string status;
tie(status_code, status) = make_http_request(url); // returns tuple<int, string>

set<string> names;
bool inserted; set<string>::iterator iter;
tie(inserted, iter)   = names.insert("Sasha");
tie(inserted, ignore) = names.insert("Sasha"); // assigns only to 'inserted'

The whole thing looks suspiciously hard but it’s just a matter of creating a tuple with lvalue references! These references can then be assigned by the assignment operator, which we already have. So here goes:

template  <typename... Types>
tuple<Types&...> tie(Types&... elements)
{
  return tuple<Types&...>(elements...);
}

But what about this ‘ignore’ thing? It’s simply an instance of a type that ignores any assignment attempts:

struct ignore_t
{
  template <typename U>
  ignore_t& operator=(U&&)
  {
    return *this;
  }
} ignore;

Finally, we have make_tuple, which is a convenience method that packages a collection of elements into a tuple. I’ll leave it to you as homework (there’s a minor subtlety that has to do with how make_tuple treats std::reference_wrapper, but it’s mostly a technicality). We have a lot to do in our final two installments — we are going to implement tuple_cat, which concatenates an arbitrary number of tuples. Stay tuned!

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. Pingback: Implementing std::tuple - Part 6: tuple_cat, Take 1