Working with pairs and tuples
In many cases you will want to associate two items together; for example, an associative container allows you to create a type of array where items other than numbers are used as an index. The <utility> header file contains a templated class called pair, which has two data members called first and second.
template <typename T1, typename T2>
struct pair
{
T1 first;
T2 second;
// other members
};
Since the class is templated, it means that you can associate any items, including pointers or references. Accessing the members is simple since they are public. You can also use the get templated function, so for a pair object p you can call get<0>(p) rather than p.first. The class also has a copy constructor, so that you can create an object from another object, and a move constructor. There is also a function called make_pair that will deduce the types of the members from the parameters:
auto name_age = make_pair("Richard", 52);
Be wary because the compiler will use the type that it thinks is most appropriate; in this case the pair object created will be pair<const char*, int>, but if you want the first item to be a string, it is simpler to use the constructor. You can compare pair objects; the comparison is performed on the first member and only if they are equal is the second then compared:
pair <int, int> a(1, 1);
pair <int, int> a(1, 2);
cout << boolalpha;
cout << a << " < " << b << " " << (a < b) << endl;
The parameters can be references:
int i1 = 0, i2 = 0;
pair<int&, int&> p(i1, i2);
++p.first; // changes i1
The make_pair function will deduce the types from the parameters. The compiler cannot tell the difference between a variable and a reference to a variable. In C++11 you can use the ref function (in <functional>) to specify that the pair will be for references:
auto p2 = make_pair(ref(i1), ref(i2));
++p2.first; // changes i1
If you want to return two values from a function, you could do it via parameters passed by reference, but the code is less readable because you expect a return value to come through the return of a function rather than through its parameters. The pair class allows you to return two values in one object. One example is the minmax function in <algorithm>. This returns a pair object containing the parameters in order of the smallest first, and there is an overload where you can provide a predicate object if the default operator < should not be used. The following will print {10,20}:
auto p = minmax(20,10);
cout << "{" << p.first << "," << p.second << "}" << endl;
The pair class associates two items. The Standard Library provides the tuple class that has a similar functionality, but since the template is variadic it means that you can have any number of parameters of any type. However, the data members are not named as in pair, instead you access them via the templated get function:
tuple<int, int, int> t3 { 1,2,3 };
cout << "{"
<< get<0>(t3) << "," << get<1>(t3) << "," << get<2>(t3)
<< "}" << endl; // {1,2,3}
The first line creates a tuple that holds three int items and it is initialized using an initialize list (you could use constructor syntax). The tuple is then printed to the console by accessing each data member in the object using a version of the get function where the template parameter indicates the index of the item. Note that the index is a template parameter, so you cannot provide it at runtime using a variable. If this is what you want to do, then it is a clear indication that you need to use a container such as vector.
The get function returns a reference, so this can be used to change the value of the item. For a tuple t3, this code changes the first item to 42 and the second to 99:
int& tmp = get<0>(t3);
tmp = 42;
get<1>(t3) = 99;
You can also extract all the items with one call, by using the tie function:
int i1, i2, i3;
tie(i1, i2, i3) = t3;
cout << i1 << "," << i2 << "," << i3 << endl;
The tie function returns a tuple in which each parameter is a reference and initialized to the variables that you pass as parameters. The previous code is easier to understand if you write it like this:
tuple<int&, int&, int&> tr3 = tie(i1, i2, i3);
tr3 = t3;
A tuple object can be created from a pair object, and so you can use the tie function to extract values from a pair object too.
There is a helper function called make_tuple, which will deduce the types of the parameters. As with the make_pair function, you have to be wary of the deductions, so a floating-point number will be deduced to be a double and an integer will be an int. If you want the parameters to be references to specific variables, you can use the ref function or the cref function for a const reference.
You can compare tuple objects as long as there are equal numbers of items and equivalent types. The compiler will refuse to compile comparisons of tuple objects that have different numbers of items or if the types of the items of one tuple objects cannot be converted to the types of the other tuple object.