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<T
1, T
2>
hold two elements: one of type T
1 and one of type T
2. The tuple
template extends this by taking an arbitrary number of type arguments. Objects of type tuple<T
1, T
2, …, T
N>
hold N elements of types T
1, T
2, …, T
N, 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.
<tuple>
Synopsisnamespace 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>&);
} }
tuple
Class Templatetemplate<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
};
tuple
ObjectYou 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;
}
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;
}
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;
}
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;
}
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
.
<utility>
Synopsisnamespace 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> >;
} }
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;
}
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
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
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
.
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
.
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.
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.
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.
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?
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?
18.225.234.24