Constructing Tuples The construction of a tuple involves declaring the types and, optionally, providing a list of initial values of compatible types.[1] [1] The constructor arguments d
Trang 1Usage
Tuples live in namespace tuples, which in turn is inside namespace boost Include
"boost/tuple/tuple.hpp" to use the library The relational operators are defined in the header "boost/tuple/tuple_comparison.hpp" Input and output of tuples are defined in "boost/tuple/tuple_io.hpp" A few of the key tuple components (tie and make_tuple) are also available directly in namespace boost In this section, we'll cover how tuples are used in some typical scenarios, and how it is possible to
extend the functionality of the library to best fit our purposes We'll start with the construction of tuples, and gradually move on to topics that include the details of how tuples can be utilized
Constructing Tuples
The construction of a tuple involves declaring the types and, optionally, providing
a list of initial values of compatible types.[1]
[1]
The constructor arguments do not have to be of the exact type specified for the elements when specializing the tuple so long as they are implicitly convertible to those types
boost::tuple<int,double,std::string>
triple(42,3.14,"My first tuple!");
The template parameters to the class template tuple specify the element types The preceding example shows the creation of a tuple with three types: an int, a double, and a std::string Providing three parameters to the constructor initializes the values
of all three elements It's also possible to pass fewer arguments than there are
elements, which results in the remaining elements being default initialized
boost::tuple<short,int,long> another;
In this example, another has elements of types short, int, and long, and they are all initialized to 0.[2] Regardless of the set of types for your tuples, this is how they are defined and constructed So, if one of your tuple's element types is not default constructible, you need to initialize it yourself Compared to defining structs,
tuples are much simpler to declare, define, and use There's also the convenience function, make_tuple, which makes creating tuples easier still It deduces the types, relieving you from the monotony (and chance of error!) of specifying them
explicitly
Trang 2Within the context of a template, T() for a built-in type means initialization with zero
boost::tuples::tuple<int,double> get_values() {
return boost::make_tuple(6,12.0);
}
The function make_tuple is analogous to std::make_pair By default, make_tuple sets the types of the elements to non-const, non-referencethat is, the plain,
underlying types of the arguments For example, consider the following variables:
int plain=42;
int& ref=plain;
const int& cref=ref;
These three variables are named after their cv-qualification (constness) and
whether they are references The tuples created by the following invocations of make_tuple all have one int element
boost::make_tuple(plain);
boost::make_tuple(ref);
boost::make_tuple(cref);
This isn't always the right behavior, but on most occasions it is, which is the reason why it's the default To make an element of a tuple to be of reference type, use the function boost::ref, which is part of another Boost library called Boost.Ref The following three lines use the variables that we declared earlier, but this time the tuples have an int& element, except for the last, which has a const int& element (we can't remove the constness of cref):
boost::make_tuple(boost::ref(plain));
boost::make_tuple(boost::ref(ref));
boost::make_tuple(boost::ref(cref));
If the elements should be const references, use boost::cref from Boost.Ref Here, the three tuples have one const int& element:
boost::make_tuple(boost::cref(plain));
boost::make_tuple(boost::cref(ref));
boost::make_tuple(boost::cref(cref));
Trang 3It's probably obvious, but ref and cref have plenty of uses in other contexts too In fact, they were created as a part of the Boost.Tuple library, but were later moved to
a separate library because of their general utility
Accessing tuple Elements
The elements of a tuple are accessed either through the tuple member function get
or the free function get They both require a constant integral expression
designating the index of the element to retrieve
#include <iostream>
#include <string>
#include "boost/tuple/tuple.hpp"
int main() {
boost::tuple<int,double,std::string>
triple(42,3.14,"The amazing tuple!");
int i=boost::tuples::get<0>(triple);
double d=triple.get<1>();
std::string s=boost::get<2>(triple);
}
In the example, a tuple with three elements with the innovative name triple was created triple contained an int, a double, and a string, which were retrieved
through the get functions
int i=boost::tuples::get<0>(triple);
Here, you see the free function get at work It takes a tuple as its one argument Note that supplying an invalid index causes an error at compilation time The precondition is that the index be a valid index for the tuple type
double d=triple.get<1>();
This code shows using the member function get The preceding line could also have been written like this:
double& d=triple.get<1>();
The preceding binding to a reference works because get always returns a reference
to the element If the tuple, or the type, is const, a const reference is returned The two functions are equivalent, but on some compilers only the free function works
Trang 4correctly The free function has the advantage of providing a consistent extraction style for types other than tuple One advantage of accessing the elements of tuples
by index rather than by name is that it enables generic solutions, because there are
no dependencies on a certain name, but only to an index More on this later
Tuple Assignment and Copy Construction
tuples can be assigned and copy constructed, providing that there are suitable conversions between the types of the elements in the two tuples To assign or copy tuples, member-wise assignment or copying is performed, so the two tuples must have the same number of elements The elements in the source tuple must be convertible to those of the destination tuple The following example shows how this works
#include <iostream>
#include <string>
#include "boost/tuple/tuple.hpp"
class base {
public:
virtual ~base() {};
virtual void test() {
std::cout << "base::test()\n";
}
};
class derived : public base {
public:
virtual void test() {
std::cout << "derived::test()\n";
}
};
int main() {
boost::tuple<int,std::string,derived> tup1(-5,"Tuples");
boost::tuple<unsigned int,std::string,base> tup2;
tup2=tup1;
tup2.get<2>().test();
std::cout << "Interesting value: "
<< tup2.get<0>() << '\n';
const boost::tuple<double,std::string,base> tup3(tup2);
tup3.get<0>()=3.14;
}
Trang 5The example begins by defining two classes, base and derived, which are used as elements of two tuple types The first tuple contains three elements of types, int, std::string, and derived The second tuple consists of three elements of the
compatible types unsigned int, std::string, and base Consequently, the two tuples meet the requirements for assignment, which is why tup2=tup1 is valid In that assignment, the third element of tup1, which is of type derived, is assigned to the third element of tup2, which is of type base The assignment succeeds, but the derived object is sliced, so this defeats polymorphism
tup2.get<2>().test();
That line extracts a base&, but the object in tup2 is of type base, so it winds up calling base::test We could have made the behavior truly polymorphic by
changing the tuples to contain references or pointers to base and derived,
respectively Note that numeric conversion dangers (loss of precision, positive and negative overflow) apply when converting between tuples as well These
dangerous conversions can be made safe with the help of the Boost.Conversion library, covered in "Library 2: Conversion."
The next line in the example copy-constructs a new tuple, tup3, with different, but still compatible types, from tup2
const boost::tuple<double,std::string,base> tup3(tup2);
Note that tup3 is declared const This implies that there is an error in the example See if you can you spot it I'll wait… Did you see it? Here it is:
tup3.get<0>()=3.14;
Because tup3 is const, get returns a const double& This means that the assignment statement is ill-formed, and the example doesn't compile Assignment and copy construction between tuples are intuitive, because the semantics are exactly the same for the tuples as for the individual elements By way of example, let's see how to give polymorphic behavior to the derived-to-base assignment between tuples
derived d;
boost::tuple<int,std::string,derived*>
tup4(-5,"Tuples",&d);