Tuple

Tuple construction

A tuple (or n-tuple) is a fixed size collection of elements. Pairs, triples, quadruples etc. are tuples. In a programming language, a tuple is a data object containing other objects as elements. These element objects may be of different types.

Tuples are convenient in many circumstances. For instance, tuples make it easy to define functions that return more than one value. Some programming languages, such as ML, Python and Haskell, have built-in tuple constructs. Unfortunately C++ does not. To compensate for this "deficiency", the Boost Tuple Library implements a tuple construct using templates.


#include "boost/tuple/tuple.hpp"

// Comparison operators can be included with: 
#include "boost/tuple/tuple_comparison.hpp"

// To use tuple input and output operators: 
#include "boost/tuple/tuple_io.hpp"

A tuple type is an instantiation of the tuple template. The template parameters specify the types of the tuple elements. The current version supports tuples with 0-10 elements. There are natural limitations for element types that cannot be be copied, or that are not default constructible (see 'Constructing tuples' below).


tuple<int, double>() 
tuple<int, double>(1) 
tuple<int, double>(1, 3.14)

class X {
  X(); 
public:
  X(std::string);
};

tuple<X,X,X>()             // error: no default constructor for X
tuple<X,X,X>(string("Jaba"), string("Daba"), string("Duu")) // ok

If no initial value for an element is provided, it is default initialized (and hence must be default initializable). In particular, reference types do not have a default initialization:


tuple<double&>()                // error: reference must be 
                                // initialized explicitly
double d = 5; 
tuple<double&>(d)               // ok

tuple<double&>(d+3.14)          // error: cannot initialize 
                                // non-const reference with a temporary

tuple<const double&>(d+3.14)    // ok, but dangerous: 
                                // the element becomes a dangling reference 

There is a make_tuple helper function. This is like std::make_pair. This makes the construction more convenient, saving the programmer from explicitly specifying the element types:


tuple<int, int, double> add_multiply_divide(int a, int b) 
{
  return make_tuple(a+b, a*b, double(a)/double(b));
}

The make_tuple invocation results in a tuple of type tuple<A, B>.

Sometimes the plain non-reference type is not desired, e.g. if the element type cannot be copied. Therefore, the programmer can control the type deduction and state that a reference to const or reference to non-const type should be used as the element type instead. This is accomplished with two helper template functions: ref and cref. Any argument can be wrapped with these functions to get the desired type. The mechanism does not compromise const correctness since a const object wrapped with ref results in a tuple element with const reference type (see the fifth code line below). For example:


A a; B b; const A ca = a;

make_tuple(cref(a), b);      // creates tuple<const A&, B>
make_tuple(ref(a), b);       // creates tuple<A&, B>
make_tuple(ref(a), cref(b)); // creates tuple<A&, const B&>
make_tuple(cref(ca));        // creates tuple<const A&>
make_tuple(ref(ca));         // creates tuple<const A&>

Arrays are also possible tags:


make_tuple("Donald", "Daisy");

tuple<const char (&)[7], const char (&)[6]>

And also function pointers:


void f(int i);
  ...
make_tuple(&f); // tuple<void (*)(int)>
  ...
tuple<tuple<void (&)(int)> > a(f) // ok
make_tuple(f);                    // not ok

Tuple operations

Tuple elements are accessed with the expression: t.get<N>()


double d = 2.7; A a;
tuple<int, double&, const A&> t(1, d, a);
const tuple<int, double&, const A&> ct = t;
  ...
int i = get<0>(t); i = t.get<0>();        // ok
int j = get<0>(ct);                       // ok
get<0>(t) = 5;                            // ok 
get<0>(ct) = 5;                           // error, can't assign to const 
  ...
double e = get<1>(t); // ok   
get<1>(t) = 3.14;     // ok 
get<2>(t) = A();      // error, can't assign to const 
A aa = get<3>(t);     // error: index out of bounds 
  ...
++get<0>(t);  // ok, can be used as any variable

A tuple can be copy constructed from another tuple, provided that the element types are element-wise copy constructible. Analogously, a tuple can be assigned to another tuple, provided that the element types are element-wise assignable. For example:


class A {};
class B : public A {};
struct C { C(); C(const B&); };
struct D { operator C() const; };
tuple<char, B*, B, D> t;
  ...
tuple<int, A*, C, C> a(t); // ok 
a = t;                     // ok 

Note: there are memberwise conversions, default and user-defined ones. Also there is a conversion from std::pair.

There are comparison operators between tuples with same dimensions:


tuple<std::string, int, A> t1(std::string("same?"), 2, A());
tuple<std::string, long, A> t2(std::string("same?"), 2, A());
tuple<std::string, long, A> t3(std::string("different"), 3, A());

bool operator==(A, A) { std::cout << "All the same to me..."; return true; }

t1 == t2; // true
t1 == t3;               // false, does not print "All the..."

The operators define lexicographical comperisons.

The function tie is also used to construct elements:


int i; char c; double d; 
tie(i, c, d) = make_tuple(1,'a', 5.5);
std::cout << i << " " <<  c << " " << d;

// creates: tuple<int&, char&, double&>
// same as: make_tuple(ref(i), ref(c), ref(a))

There is a possibility to ignore one or more fields.


char c;
tie(tuples::ignore, c) = std::make_pair(1, 'a');

Input/Output

There is a memberwise output and input operator.


tuple<float, int, std::string> a(1.0f,  2, std::string("Howdy folks!");

cout << a; 
// outputs the tuple as: (1.0 2 Howdy folks!)

There are 3 manipulators for changing the default behavior:


set_open(char)      // defines the character that is output before the first element.
set_close(char)     // defines the character that is output after the last element.
set_delimiter(char) // defines the delimiter character between elements.

cout << tuples::set_open('[') << tuples::set_close(']') << tuples::set_delimiter(',') << a;

The same works for input operation.