C++ Tutorial: Use std::tuple To Simulate Compact Heterogeneous Containers

Introduction

Standard C++ programming containers classes such as vector and list are homogeneous, meaning they can store only one type of objects at a time. However, in some cases it’s more convenient to use a container of heterogeneous objects, which is where std::tuple comes in handy. std::tuple can store up to 10 objects of different types.

Suppose you need to design a function that retrieves three values from a database: an index number representing the month of the year, and two floating point numbers that represent the consumer’s price index (CPI) of the relevant month, and the CPI of the previous month. Although you can split this function into two, it’s more efficient and convenient to have one function that retrieves these values in one shot. Your first step consists of defining a tuple type i.e, a specialization of the class template std::tuple. Here’s an example:

  #include <tuple>
  using namespace std;

  typedef tuple <int, double, double> CPIs;

Here’s the function declaration:

  CPIs get_cpis(const Date& d);

In the absence of explicit initializers for the tuple’s elements, the elements will be default-initialized:

  tuple <double, char> t2(3.55, 'a');// explicit
  tuple <double, int, string> t3; // <0.0,0,string()>

Helper Functions

The Standard Library defines meta-functions and helper functions for handling tuples conveniently. Meta-functions use template meta-programming to compute their results at compile time. Helper functions are free-standing functions with short and intuitive names. Let’s look at some of these.

To get the number of elements that a tuple type has, use the tuple_size() meta-function:

  #include <tuple>
  using namespace std;

  int elements=tuple_size<CPIs>::value;//3

To automate the creation of a tuple object, use the make_tuple() helper function. make_tuple() deduces the types of its arguments to create a tuple type. It returns a tuple object initialized with those arguments:

  CPIs mycpi=make_tuple(2, 0.1, 0.3);

To get the type of a tuple element, use the tuple_element() meta-function. This function is useful for traversing a tuple or when you want to create copies of a tuple’s elements without knowing the elements’ types in advance. tuple_element() takes an index and the tuple type (remember that tuples use zero-based indexing). In the following example, tuple_element() retrieves the type of the first tuple element. The result is used for declaring val, an object whose type is the same as that of the tuple’s first element:

  tuple_element <0, tuple<int, int, char> >::type val;//int

What if you need to access the actual value of a tuple element rather than its type? Use the get<n> function template. Get takes an index and returns the corresponding element’s value. The following listing uses tuple_element() to declare objects of the types of a tuple object’s elements and then calls get<n> to copy their corresponding values:

  CPIs cpi=make_tuple(4, 0.1, -0.2);
  tuple_element <0,CPIs>::type val1;//int
  tuple_element <1,CPIs>::type val2;//double
  tuple_element <2,CPIs>::type val3;//double

  val1=get<0> (mycpi);//4
  val2=get<1> (mycpi);//0.1
  val3=get<2> (mycpi);//-0.2

Putting it all Together

Suppose you’re designing a stock quote search engine that accepts a free text query. The engine retrieves the current stock price along with the stock’s symbol. To represent the stock prices, you use a pair of integral values for the dollars and cents (a pair of integers rather than double guarantees accurate comparisons among other things), and a string for the stock symbol. You can represent the stock price like this:

  typedef tuple<int,int,string> StockPrice1;

This design is simple but not descriptive enough — the reader has to guess that the first two integers are construed as a pair. An alternative design would use a layered structure:

  typedef tuple<int,int> Currency;
  typedef tuple<Currency, double, string > StockPrice2;

Regardless of your favorite representation, both designs exhibit the beauty of tuples. For example, to compare two StockPrice records you don’t need to overload any operators. std::tuple already includes the relevant overloaded operators for you:

  StockPrice1 get_quote(const string& query);
  StockPrice1 a(620,24,"GOOG");
  StockPrice1 b=get_quote("Google Inc.");
  if (a!=b) //is it the same quote?
...

As an aside, you’re probably wondering why I didn’t use std::pair to represent Currency. The truth is that a pair is nothing but a tuple containing two members. Historically, std::pair inspired the authors of std::tuple to design a more generalized notion of a fixed-size heterogeneous container. Of course, it doesn’t mean that you’re advised to avoid std::pair;

Conclusion

Tuples were added to C++ programming as part of the Standard Library Technical Report One (TR1 for short) which is a fancy name for what would otherwise be dubbed a service pack for the C++98 Standard Library. Virtually, every standard-compliant C++ compiler supports std::tuple today. Additionally, tuples (as all other TR1 features) are highly portable, so you can use them without hesitation in cross-platform code.

The main advantage of std::tuple is automation. Instead of inventing a heterogeneous container on your own, std::tuple will be convenient so long as you don’t need more than 10 elements and don’t expect to insert or remove elements dynamically.

Related Articles

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read