Chapter 1. Tuples

You cannot conceive of the many without the one.

Parmenides
PLATO

The class template tuple is a generalization of the standard C++ library class template pair. The pair template takes two type arguments; objects of type pair<T1, T2> hold two elements: one of type T1 and one of type T2. The tuple template extends this by taking an arbitrary number of type arguments. Objects of type tuple<T1, T2, …, TN> hold N elements of types T1, T2, …, TN, respectively.[1]

Example 1.1. Tuple Declarations (tuples/typedefs.cpp)


#include <tuple>
using std::tr1::tuple;

tuple<> none;                      // holds no elements
tuple<int> one;                    // holds one element, of type int

int i; double d;
tuple<int&, const double&>
    two(i, d);                      // holds two elements, one of
                                   // type reference to int and one
                                   // of type reference to const double
tuple<int, int, int, int, int,
   int, int, int, int, int> ten ;  // holds ten elements, all of type int


You can create tuple objects through each tuple type’s constructors. In addition, two function templates—make_tuple and tie—return tuple objects whose types are based on the arguments to the functions. These techniques for creating tuple objects are covered in Section 1.2.1.

The function template get returns a reference to an element of a tuple object. The result can be used to examine the value of the element and to modify an element of a non-const tuple object. The values of elements can also be changed by assigning one tuple object to another. These access mechanisms are covered in Section 1.2.2.

You can ask about the size of a specialization of tuple and the type of each of its elements through the class templates tuple_size and tuple_element. These templates are covered in Section 1.2.3.

You can compare two tuple objects of the same size for equality and for relative order. See Section 1.2.4.

1.1. Header <tuple> Synopsis

namespace std {
 namespace tr1 {
    // TUPLE CLASS TEMPLATES
template<class T1, class T2, …, class TN>
  class tuple;
template<class Tuple> struct tuple_size;
template<int Idx, class Tuple> struct tuple_element;

    // TUPLE FUNCTION TEMPLATES
template<int Idx, class T1, class T2, …, class TN>
  RI get(tuple<T1, T2, …, TN>&);
template<int Idx, class T1, class T2, …, class TN>
  RI get(const tuple<T1, T2, …, TN>&);
template<class T1, class T2, …, class TN>
  tuple<V1, V2, …, VN>
  make_tuple(const T1 &, const T2 &, …, const TN &);
template<class T1, class T2, …, class TN>
  tuple<T1&, T2 &, …, TN&>
  tie(T1 &, T2 &, …, TN &);

     // CONSTANTS
const unspecified ignore;

    // COMPARISON OPERATORS
template<class T1, class T2, …, class TN,
    class U1, class U2, …, class UN>
  bool operator==(const tuple<T1, T2, …, TN>&,
    const tuple<U1, U2, …, UN>&);
template<class T1, class T2, …, class TN,
    class U1, class U2, …, class UN>
  bool operator!=(const  tuple<T1, T2, …, TN>&,
    const tuple<U1, U2, …, UN>&);
template<class T1, class T2, …, class TN,
    class U1, class U2, …, class UN>
  bool operator<(const  tuple<T1, T2, …, TN>&,
    const tuple<U1, U2, …, UN>&);
template<class T1, class T2, …, class TN,
    class U1, class U2, …, class UN>
  bool operator<=(const tuple<T1, T2, …, TN>&,
    const tuple<U1, U2, …, UN>&);
template<class T1, class T2, …, class TN,
    class U1, class U2, …, class UN>
  bool operator>(const tuple<T1, T2, …,  TN>&,
    const tuple<U1, U2, …, UN>&);
template<class T1, class T2, …, class TN,
    class U1, class U2, …, class UN>
  bool operator> =(const tuple<T1, T2, …, TN>&,
    const tuple<U1, U2, …, UN>&);
} }

1.2. The tuple Class Template

template<class T1, class T2, …, class TN>
class tuple {
public:
  // CONSTRUCTORS
  tuple ();
  explicit tuple(P1, P2, …, PN);             // when N> 0
  tuple(const tuple &);
  template<class U1, class U2, …, class UN>
    tuple(const  tuple<U1, U2, …, UN>&);
  template<class U1, class   U2>
    tuple(const  pair <U1, U2>&);             // when N == 2

  // ASSIGNMENT OPERATORS
  tuple & operator=(const tuple &);
  template<class U1, class U2, …, class UN>
    tuple & operator = (const tuple<U1, U2, …, UN>&);
  template< class U1, class U2>
    tuple & operator = (const pair <U1, U2>&);  // when N == 2
  };

1.2.1. Creating a tuple Object

You can create a tuple object in several ways. If all the element types have default constructors, a tuple holding those element types also has a default constructor; it constructs a tuple object with all its elements constructed with their respective default constructors. In addition, each tuple type that holds one or more elements has a constructor that takes as many arguments as the type has elements; it constructs each stored element from the corresponding argument. Obviously, each argument type must be convertible to the appropriate element type. Finally, a tuple object can be constructed by copying another tuple object with the same number of elements and, when the tuple object that’s being constructed holds two elements, by copying a pair object.

Example 1.2. Tuple Constructors (tuples/ctors.cpp)


#include <utility>
#include <tuple>
using std::tr1::tuple; using std::pair;

class C {
public:
  C (): val (0) {}
  C(int i) : val (i) {}
private :
  int val;
};

tuple<> t0;                   // default constructor
tuple<int>t1;                 // default constructor; element
                              // not initialized
tuple<int> t2  (3);           // element initialized to 3
tuple<C> t3;                  // element initialized to C()
tuple<C> t4(C   (1));         // element initialized to C(1)
tuple<C, C> t5 (1,2);         // first element initialized to C(1)
                              // second element initialized to C(2)
tuple<double>  t6(t2);        // element initialized to 3.0
pair <int, int> p0 (3, 4);    // first element initialized to 3
                              // second element initialized to 4
tuple<C, C> t7(p0);           // first element initialized to C(3)
                              // second element initialized to C(4)


Sometimes, it’s inconvenient to have to list the type arguments for tuple. When you know the values that you want a tuple object to hold, you can use the function make_tuple to create a tuple object that holds copies of its arguments.

Example 1.3. Function Template maketuple (tuples/maketuple.cpp)


#include <tuple>
#include <typeinfo>
#include <iostream>
using std::tr1::tuple; using std::tr1::make_tuple;

template <class T> void show_type (T)
    {
    std::cout << typeid (T). name() << " ";
    }

int main()
    {
    int i = 3;
    int & j = i;
    show_type (make_tuple ());            // returns tuple<>
    show_type (make_tuple (1,   3.14));   // returns tuple<int, double>
    show_type (make_tuple (i, j));        // returns tuple<int, int>
    return 0;
    }


If you ran this example and managed to wade through the lengthy names of the tuple types that your compiler generated, you probably noticed that the last call to make_tuple returns an object of type tuple<int, int>, even though the second argument to make_tuple is a reference to int. The function template make_tuple doesn’t distinguish between objects and references to objects. Both result in an element with the type of the object.

To create a tuple object that holds references, use the TR1 library function templates ref and cref, defined in the header <functional>. These functions have other uses that we discuss in detail later (see Section 8.1). For now, ref is a wrapper that tells make_tuple that the corresponding element type is a reference to the type of the argument to ref. Similarly, cref tells make_tuple to create an element that’s a reference to a const value type.

Example 1.4. Use of ref and cref (tuples/refcref.cpp)


#include <tuple>
#include <functional>                    // for ref, cref
using std::tr1::make_tuple;
using std::tr1::ref; using std::tr1::cref;

void test ()
    {
    int i = 17;
    int j = 3;
    make_tuple (ref (i), cref (j));     // returns tuple<int&, const int&>
                                        // first element is reference to i
                                        // second element is reference to j
    }


Sometimes you’ll want to create a tuple object that holds only references to objects. As we just saw, you can use ref to do this, but that can turn into a lot of typing. The function template tie is a convenient way of creating a tuple object that holds references to its arguments. Passing the value ignore as an argument tells tie that the tuple object it returns should ignore assignments to the element that corresponds to that argument.

Example 1.5. Function Template tie (tuples/tie.cpp)


#include <tuple>
#include <iostream>
using std::tr1::make_tuple; using std::tr1::tie;
using std::tr1::ignore;

int i = 1;
int j = 2;
int k = 3;

void  show ()
  {
  std::cout << i << ' ' << j << ' ' << k << ' ';
  }

int main()
  {
  show ();                      // 1 2 3
  tie (i, ignore, k) =
    make_tuple (5, 6, 7);
  show ();                      // 5 2 7
  return 0;
  }


1.2.2. Access

You can change the values in a tuple object by assigning the value of another tuple object to it. The two objects must hold the same number of elements, and the type of each of the elements in the source object must be convertible to the type of the corresponding element in the target object.

You can also change the values of a tuple object that holds two elements by assigning from a pair object.

Example 1.6. Assigning tuple Objects (tuples/assign.cpp)



#include <utility>
#include <iostream>
#include <tuple>
using std::tr1::tuple; using std::tr1::get;
using std::cout; using std::make_pair;

void show(int i, int j, const   tuple<int, int &, int>& t)
  {
  cout << i <<  '  '  << j << ":"
       << get <0>(t)   << ' '
       << get <1>(t)   << ' '
       << get <2>(t)   << ' ';
  }

void show(const tuple<int, int>& t)
  {
  cout << get <0>(t)   << ' '
       << get <1>(t)   << '   ' ;
  }

int main()
  {
  int i = 1, j = 2;
  tuple<int, int &, int> t0(i, j,   3);
  tuple<int, double, char> t1    (4, 5.1, ' 6 ' );
  show(i, j, t0 );                      // 1 2: 1 2 3
  t0 = t1;
  show(i, j, t0 );                      // 1 5: 4 5 6
  tuple<int, int> t2 (1,2);
  show(t2);                           // 1 2
  t2 = make_pair (3,4);
  show(t2);                           // 3 4
  return 0;
  }


The function template get takes a tuple object and returns a reference to the value of one of its elements or, when the element is itself a reference, a copy of that reference. The syntax for this function call looks a little odd. To get the nth element, you pass the value of n as a template argument.[2]

Example 1.7. Function Template get (tuples/get.cpp)


#include <tuple>
#include <iostream>
using std::tr1::tuple; using std::tr1::get;
using std::cout;

int main()
  {
  tuple<int, int> t0(1, 2);
  cout << get<0>(t0) << ' ' << get <1>(t0) << ' ';     // 1 2
  return 0;
  }


Since it returns a reference to an element of a tuple, you can use get to change the value of that element or, when the element is itself a reference, to change the value of the object that it refers to.

Example 1.8. Modifying with get (tuples/getmod.cpp)


#include <tuple>
#include <iostream>
using std::tr1::tuple; using std::tr1::get;
using std::cout;

void show (int i, int j, const tuple<int, int&, int> t)
  {
  cout << i << ' '  << j << ": "
       << get<0>(t) << ' '
       << get<1>(t) << ' '
       << get<2>(t) << ' ';
  }

int main()
  {
  int i = 1, j = 2;

  tuple<int, int&, int> t0(i, j, 3);
  show(i, j, t0);               // 1 2: 1 2 3
  get<0>(t0) = 4;               // set first element to 4
  get<1>(t0) = 5.1;             // set object referred to by
                                // second element to 5
  get<2>(t0) = '6';            // set third element to 6
  show (i, j, t0);              // 1 5: 4 5 6
  return 0;
  }


1.2.3. Type Queries

If you need to know how many elements a tuple type holds, you can use the class template tuple_size.

template<class Tuple> struct tuple_size
  {
  static const unsigned value = …;
  };

When this template is instantiated with a tuple type, its nested member value holds the number of elements in the tuple type.

Example 1.9. Class Template tuple_size (tuples/tuplesize.cpp)


#include <tuple>
#include <iostream>
using std::tr1::tuple; using std::tr1::tuple_size;
using std::cout;

typedef tuple<> tuple0;
typedef tuple<int> tuple1;
typedef tuple<int&, double&> tuple2;
typedef tuple<int, int, int, int, int> tuple5;

int main()
  {
  cout << tuple_size <tuple0>::value << ' ';    // 0
  cout << tuple_size <tuple1>::value << ' ';    // 1
  cout << tuple_size <tuple2>::value << ' ';    // 2

  cout << tuple_size <tuple5>::value << ' ';    // 5
  return 0;
  }


To determine the type of an element, use the class template tuple_-element.

template<int Idx, class Tuple> struct tuple_element
  {
  typedef … type;
  };

When this template is instantiated with the value of the index of the desired element and a tuple type, its nested member type names the type of the element.

Example 1.10. Class Template tuple_element (tuples/tupleelt.cpp)


#include <tuple>
#include <iostream>
#include <typeinfo>
using std::tr1::tuple; using std::tr1::tuple_element;
using std::cout;

typedef tuple<int> tuple1;
typedef tuple<int&, double&> tuple2;

template<class Ty>
void show_type ()
  {
  cout << typeid (Ty).name() << ' ';
  }

int main()
  {
  show_type<tuple_element <0, tuple1>::type>(); // int
  show_type<tuple_element <0, tuple2>::type>(); // int
  show_type<tuple_element <1, tuple2>::type>(); // double
  return 0;
  }


1.2.4. Comparison

Two tuple objects can be compared if they have the same number of elements and the corresponding individual elements can be compared. The usual six comparison operators— ==, !=, <, <=, >, >= —are provided. All the comparisons are short circuited: That is, comparisons are made element by element until the result is known, and no further comparisons are made. For example, when comparing two objects of type tuple<int, int> for equality, when the first object holds the values 0 and 2 and the second object holds the values 1 and 3, only the values of the first elements are compared; the equality test fails, and there is no need to compare the values of the second elements.

Comparisons for equality (t0 == t1) apply the operator == to the corresponding elements. Comparisons for inequality (t0 != t1) apply operator == to the tuples and negate the result.

The operator < is more complicated. The rule is that the comparison is a lexicographical comparison; that is, the leftmost pair of corresponding elements that are not equal determines the result.[3] However, to determine the order of two tuples, the elements are compared using only operator < on the individual elements. Two elements are equal if neither one is less than the other. To see this better, let’s compare these two objects by hand:

tuple<int, double, int> first (0, 2.0, 1);
tuple<long, float, int> second (0, 1.0, 2);

To decide whether first < second, we begin by comparing the first element of first and the first element of second:

get<0>(first) < get<0>(second) // false: both elements are 0

Since the result of the comparison was false, we look at the opposite comparison:

get<0>(second) < get<0>(first) // also false: both elements are 0

Now we know that the first elements of the two tuples are equal. We don’t yet know whether first is less than second, so we move to the next element:

get<1>(first) < get<1>(second) // false: 2.0 is not less than 1.0f

Since the result of the comparison was false, we again look at the opposite comparison:

get<1>(second) < get<1>(first) // true: 1.0f < 2.0

We end the comparison here because we found the leftmost pair of elements that are not equal, and that pair determines the order of the two tuples. Since get<1>(second) is less than get<1>(first), the tuple second is less than the tuple first, and the result of the comparison first < second is false.

The other three comparison operators are defined in terms of operator <. The expression first > second is equivalent to second < first; first <= second is equivalent to !(second < first); and first >= second is equivalent to !(first < second).

To see this in action, compile and run the following example.

Example 1.11. Comparing tuple Objects (tuples/compare.cpp)


#include <iostream>
#include <iomanip>
#include <tuple>
using std::tr1::tuple;
using std::cout; using std::boolalpha;

class C {
public:
  C(int i) : val (i) {}
  int value() const {return val;}
private:
  int val;
};

bool operator==(const C& left, const C& right)
  {
  bool res = left.value() == right.value();
  cout << "  " << left.value()
       << " == " << right.value();
  cout << " is " << res << ' ';
  return res;
  }

bool operator <(const C& left, const C& right)
  {
  bool res = left.value() < right.value();
  cout << "  " << left.value()
       << " < " << right.value();
  cout << " is " << res << ' ';
  return res;
  }

#define TEST (expr)         
  cout << #expr << ' ';    
  cout << "    result is " << (expr) << ' '

typedef tuple<C&, C&, C&> tuple1;

int main()
  {
  C a = 1;
  C b = 2;
  C c = 3;
  C d = 1;
  C e = 4;
  C f = 3;
  cout << boolalpha;                  // set alphabetic form for bool
  tuple1 t0(a, b, c);
  tuple1 t1(d, e, f);
  TEST (t0 == t1 );                   // a == d is true, b == e is false,
                                      // result is false
  TEST (t0 != t1);                    // t0 == t1 is false, result is true
  TEST (t0 < t1);                     // a  < d is false,
                                         // d  < a is false and b  < e is true,
                                         // result is true
  TEST (t1 < = t0);                   // t0  < t1 is true, result is false
  TEST (t1 > t0);                     // t0  < t1 is true, resul        t is true
  TEST (t0 > = t1);                   // t0  < t1 is true, result is false
  return 0;
  }


1.3. tuple-like Access to std::pair

For uniformity, the TR1 library adds a pair of get function templates: one taking a reference to a pair object, and the other taking a reference to a const pair object. Both return a reference to the element at the position specified by the explicit template argument. The library also adds specializations of the tuple_element and tuple_size templates, which give the element types and element count for specializations of pair.

1.3.1. Header <utility> Synopsis

namespace std {
template<class T1, class T2>
  struct pair;

  namespace tr1 {
    // PAIR FUNCTION TEMPLATES
template<int Idx, class T1, class T2>
  RI get(pair <T1, T2>&);
template<int Idx, class T1, class T2>
  RI get(const pair <T1, T2>&);

    // PAIR CLASS TEMPLATES
template<class T1, class T2>
  struct tuple_size<pair<T1, T2> >;
template<class T1, class T2>
  struct tuple_element<0, pair<T1, T2> >;
template<class T1, class T2>
  struct tuple_element<1, pair<T1, T2> >;
} }

1.3.2. Details

The overloaded get versions that take an object of type pair<T1, T2> return references to the designated element. For a pair object pr, the call get<0>(pr) returns a reference to pr.first, and the call get<1>(pr) returns a reference to pr.second.

Since a pair object always holds two elements, the specialization of tuple_size for pair types always holds a nested value of 2.

template<class T1, class T2>
struct tuple_size<pair<T1, T2> >
  {
  static const unsigned value = 2;
  };

The specialization of tuple_element for pair acts just like the version of tuple_element for tuple. The value of the template argument Idx can be only 0 or 1.

template< class T1, class T2>
struct tuple_element<0, pair<T1, T2> >
  {
  typedef T1 type;
  };

template<class T1, class T2>
  struct tuple_element<1, pair <T1, T2> >
  {
  typedef T2 type;
  };

Example 1.12. tuple-like Interface to pair (tuples/pair.cpp)


#include <iostream>
#include <typeinfo>
#include <utility>
using std::pair; using std::make_pair;
using std::tr1::get; using std::tr1::tuple_element;
using std::tr1::tuple_size;
using std::cout;

template<class  Ty>
void show (const Ty& pr)
  {
  cout << "size:   " << tuple_size<Ty>::value << ' ';
  cout << "first type:  "
    << typeid (tuple_element<0, Ty>::type).name() << ' ';
  cout << "second type: "
    << typeid (tuple_element<1, Ty>::type).name() << ' ';
  cout << "first:  " <<get<0>(pr) << ' ';
  cout << "second: " <<get<1>(pr) << ' ' << ' ';
  }

int main()
  {
  show(make_pair (1,2));
  show(make_pair (3.0,1.1 f));
  return 0;
  }


Exercises

Exercise 1

One of the biggest difficulties in writing code that uses heavily templatized types, such as tuple, is deciphering the messages that your compiler and your library implementation produce when your code has an error.

For each of the following errors, write a simple test case containing the error, and try to compile it. In the error messages, look for the key words that relate to the error in the code. This will make developing code that uses tuple much easier.

1. Attempting default construction of a tuple object when one or more of the element types does not have a default constructor

2. Attempting copy construction of a tuple<T> object from a tuple<U> object when objects of type U are not convertible to type T

3. Attempting assignment of a tuple<T> object from a tuple<U> object when objects of type U are not convertible to type T

4. Attempting copy construction or assignment to a tuple object from a tuple object with a different number of elements

5. Attempting to call get with an index that is greater than the number of elements in the tuple argument

Exercise 2

Write and compile a source file that defines the following types:

1. tuple0, a type that is a synonym for a tuple specialization that holds no elements

2. tuple1, a type that is a synonym for a tuple specialization that holds one element, of type long double

3. tuple2, a type that is a synonym for a tuple specialization that holds two elements: the first of type long double and the second of type reference to unsigned long

4. tuple7, a type that is a synonym for a tuple specialization that holds elements of the following types: char, short, int, long, float, double, long double

Exercise 3

Write a program that creates objects of type tuple<unsigned char, unsigned char, unsigned char>[4] in three ways and displays their stored values. Do this in two steps:

1. Write a function named show that takes an argument of type const tuple<unsigned char, unsigned char, unsigned char>& and displays the three values that it holds. Use the templates get<0>, get<1>, and get<2> to get the values.

2. In the main function, create the objects and pass them to show.

Exercise 4

Write a program that creates three auto variables of type int, all initialized to 0, and an auto object of type tuple<int&, int&, int&> that holds references to the three auto variables. Use the tuple object to show the values of the auto variables. Now change the values of the auto variables.

1. Write three statements that use the tuple object to change the values of the three auto variables one at a time to 1, 2, and 3. Use the tuple object to show the new values.

2. Write a single statement that uses the tuple object to change the values of the three auto variables to 4, 5, and 6. Show the new values of the auto variables. Hint: Use make_tuple.

3. Write a single statement that creates a temporary object of type tuple<int, int, int> holding the values 7, 8, and 9 and assigns the values held in that tuple object to the three auto variables. Show the new values of the auto variables. Hint: Use a temporary object of type tuple<int&, int&, int&> to change the values of the three auto variables.

4. Write a single statement that creates a temporary object of type tuple<int, int, int> holding the values 10, 11, and 12 and assigns the first and third values held in that tuple object to the first and third auto variables. Show the new values of the auto variables. Hint: Use tie.

Exercise 5

Write a program that shows the properties of several tuple instances.

1. Write a function template that can be called with a tuple object holding an arbitrary number of elements that shows how many elements the object holds. Use this function to show the sizes of several tuple types. Hint: To write a function template that takes only tuple instances of various sizes, you’d have to write a separate function for each possible size. That’s tedious and not particularly interesting. Instead, write a function template that takes an argument of any type and assumes that the type of the argument is, in fact, a tuple instance.

2. Write a function template that can be called with a tuple object holding two elements that shows the types of the elements, using typeid::name. Use this function to show the types of the elements in several tuple instances.

Exercise 6

Consider the following objects:

tuple<int, int, int> t0 (1, 2, 3);
tuple<double, double, double> t1 (1.0, 2.0, 3.0);
tuple<double, double, double> t2 (1.0, 2.1, 2.9);

1. What should the result of each of the following comparisons be?

a. t0 == t1

b. t0 == t2

c. t0 < t1

d. t0 < t2

2. Now write a program to create these three objects, perform each of the preceding comparisons, and report the result. Compile and run the program.

Exercise 7

Some math libraries implement the sin and cos functions by calling a single function that computes both values, then picking the appropriate result.

1. Write the prototype for that single function, with the name sincos, taking one argument of type double and returning the two double values in a tuple object.

2. Write a function named split that takes one argument of type double and two arguments of type double&, calls sincos, and copies the two result values into its two reference arguments, without using any tuple variables.

3. Assume that the function sincos has three overloaded versions, one taking an argument of type float and returning values of type float, one taking an argument of type double and returning values of type double, and one taking an argument of type long double and returning values of type long double. Rewrite the function split as a template that calls the appropriate version of sincos, depending on the type of its arguments.

Exercise 8

Suppose that we want to calculate the distance from the origin to a point in one-, two-, or three-dimensional space, with the location of the point held in an appropriately sized tuple object.

1. Write a function named distance1[5] that takes an object of type tuple<double> and returns the distance from the origin to the point in one-dimensional space.

2. Write a function named distance2 that takes an object of type tuple <double, double> and returns the distance from the origin to the point in two-dimensional space.

3. Write a function named distance3 that takes an object of type tuple<double, double, double> and returns the distance from the origin to the point in three-dimensional space.

4. Test the three distance functions with the following values:

a. make_tuple(1.0)

b. make_tuple(3.0, 4.0)

c. make_tuple(3.5, 4.5, 5.5)

5. What happens if you call any of the distance functions with a tuple object that holds no elements?

Exercise 9

Obviously, writing a separate function for each size tuple object will get pretty tedious. Since the code is so repetitive, there ought to be a way to write a single set of function templates that can calculate the distance from the origin to a point in n-dimensional space, given a representation of the location of that point as a tuple object with n elements. With a little template metaprogramming, the solution is fairly simple, although getting to it can be confusing.

We’ll need a function template named distance that takes an arbitrary type T. If we assume that T is a specialization of tuple, we know from the previous exercises that we need to compute the sum of the squares of the elements of T and take the square root of that sum. We’ll do that with a helper that calculates the sum of the squares of the elements of a tuple object; distance returns the square root of the value returned by that helper.

Let’s look at implementing that helper. Here’s where things start to get tricky. If the tuple that we’re processing has one element, it’s easy: The sum of the squares of the elements is simply the square of that one element. If our tuple has more than one element, the sum of the squares of its elements is the square of its first element plus the sum of the squares of the rest of its elements. This is a typical formulation for a problem that can be solved by recursion: We have a termination rule that handles the simplest case and a recursion rule that handles complex cases in terms of simpler ones. As long as each recursion results in a simpler case, we eventually bottom out at the termination case and can combine the intermediate results to get the answer. So our helper has to handle two cases: one in which we have only one element and the other in which we have more than one element. We can find out how many elements a tuple type has with the template tuple_size, and we can pick off individual elements with get. But there’s a problem here: The index that we pass to get has to be a compile-time constant. Because of the limitations of template arguments to function templates, our helper has to be a class template, not a function template.

1. Write a declaration for the class template distance_helper. The template should take two template arguments: a value elt of type size_t and a class type T. Don’t give it a body.

2. Write a partial specialization of distance_helper whose size_t argument is 0; it should still take an arbitrary type T. The specialization should have a static member function named eval that takes an argument of type const T&, assumes that T is a tuple type, and returns the square of the element at index 0 of T. This is the termination case.

3. Write the general version of the template distance_helper. It should have a static member function named eval that takes an argument of type const T&. The function returns the square of the element at elt added to the sum of the squares of the remaining elt - 1 elements. Use distance_helper<elt -1, T> to calculate that sum. This is the recursion rule.

4. Now all that’s left is to call distance_helper’s function eval from distance. Write the function distance. Caution: distance_helper takes the index of an element; the number of elements in a tuple object is not a valid index.

5. Test the distance function with the following values:

a. make_tuple(1.0)

b. make_tuple(3.0, 4.0)

c. make_tuple(3.5, 4.5, 5.5)

6. What happens if you call distance with a tuple object that holds no elements?

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

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