17.1.1. Defining and Initializing tuples

When we define a tuple, we name the type(s) of each of its members:

tuple<size_t, size_t, size_t> threeD; // all three members set to 0
tuple<string, vector<double>, int, list<int>>
    someVal("constants", {3.14, 2.718}, 42, {0,1,2,3,4,5});

When we create a tuple object, we can use the default tuple constructor, which value initializes (§ 3.3.1, p. 98) each member, or we can supply an initializer for each member as we do in the initialization of someVal. This tuple constructor is explicit7.5.4, p. 296), so we must use the direct initialization syntax:

tuple<size_t, size_t, size_t> threeD =  {1,2,3};  // error
tuple<size_t, size_t, size_t> threeD{1,2,3};      // ok

Alternatively, similar to the make_pair function (§ 11.2.3, p. 428), the library defines a make_tuple function that generates a tuple object:

// tuple that represents a bookstore transaction: ISBN, count, price per book
auto item = make_tuple("0-999-78345-X", 3, 20.00);

Like make_pair, the make_tuple function uses the types of the supplied initializers to infer the type of the tuple. In this case, item is a tuple whose type is tuple<const char*, int, double>.

Accessing the Members of a tuple

A pair always has two members, which makes it possible for the library to give these members names (i.e., first and second). No such naming convention is possible for tuple because there is no limit on the number of members a tuple type can have. As a result, the members are unnamed. Instead, we access the members of a tuple through a library function template named get . To use get we must specify an explicit template argument (§ 16.2.2, p. 682), which is the position of the member we want to access. We pass a tuple object to get, which returns a reference to the specified member:

auto book = get<0>(item);      // returns the first member of item
auto cnt = get<1>(item);       // returns the second member of item
auto price = get<2>(item)/cnt; // returns the last member of item
get<2>(item) *= 0.8;           // apply 20% discount

The value inside the brackets must be an integral constant expression (§ 2.4.4, p. 65). As usual, we count from 0, meaning that get<0> is the first member.

If we have a tuple whose precise type details we don’t know, we can use two auxilliary class templates to find the number and types of the tuple’s members:

typedef decltype(item) trans; // trans is the type of item
// returns the number of members in object's of type trans
size_t sz = tuple_size<trans>::value;  // returns 3
// cnt has the same type as the second member in item
tuple_element<1, trans>::type cnt = get<1>(item); // cnt is an int

To use tuple_size or tuple_element, we need to know the type of a tuple object. As usual, the easiest way to determine an object’s type is to use decltype2.5.3, p. 70). Here, we use decltype to define a type alias for the type of item, which we use to instantiate both templates.

tuple_size has a public static data member named value that is the number or members in the specified tuple. The tuple_element template takes an index as well as a tuple type. tuple_element has a public type member named type that is the type of the specified member of the specified tuple type. Like get, tuple_element uses indices starting at 0.

Relational and Equality Operators

The tuple relational and equality operators behave similarly to the corresponding operations on containers (§ 9.2.7, p. 340). These operators execute pairwise on the members of the left-hand and right-hand tuples. We can compare two tuples only if they have the same number of members. Moreover, to use the equality or inequality operators, it must be legal to compare each pair of members using the == operator; to use the relational operators, it must be legal to use <. For example:

tuple<string, string> duo("1", "2");
tuple<size_t, size_t> twoD(1, 2);
bool b = (duo == twoD); // error: can't compare a size_t and a string
tuple<size_t, size_t, size_t> threeD(1, 2, 3);
b = (twoD < threeD);    // error: differing number of members
tuple<size_t, size_t> origin(0, 0);
b = (origin < twoD);    // ok: b is true


Image Note

Because tuple defines the < and == operators, we can pass sequences of tuples to the algorithms and can use a tuple as key type in an ordered container.



Exercises Section 17.1.1

Exercise 17.1: Define a tuple that holds three int values and initialize the members to 10, 20, and 30.

Exercise 17.2: Define a tuple that holds a string, a vector<string>, and a pair<string, int>.

Exercise 17.3: Rewrite the TextQuery programs from § 12.3 (p. 484) to use a tuple instead of the QueryResult class. Explain which design you think is better and why.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.134.17