Chapter 22. Boost.Tuple

The library Boost.Tuple provides a class called boost::tuple, which is a generalized version of std::pair. While std::pair can only store exactly two values, boost::tuple lets you choose how many values to store.

The standard library has provided the class std::tuple since C++11. If you work with a development environment supporting C++11, you can ignore Boost.Tuple because boost::tuple and std::tuple are identical.

Example 22.1. boost::tuple replacing std::pair

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <string>
  4. #include <iostream>
  5. int main()
  6. {
  7. typedef boost::tuple<std::string, int> animal;
  8. animal a{"cat", 4};
  9. std::cout << a << '\n';
  10. }

To use boost::tuple, include the header file boost/tuple/tuple.hpp. To use tuples with streams, include the header file boost/tuple/tuple_io.hpp. Boost.Tuple doesn’t provide a master header file that automatically includes all others.

boost::tuple is used in the same way std::pair is. In Example 22.1, a tuple containing one value of type std::string and one value of type int is created. This type is called animal, and it stores the name and the number of legs of an animal.

While the definition of type animal could have used std::pair, objects of type boost::tuple can be written to a stream. To do this you must include the header file boost/tuple/tuple_io.hpp, which provides the required operators. Example 22.1 displays (cat 4).

Example 22.2. boost::tuple as the better std::pair

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <string>
  4. #include <iostream>
  5. int main()
  6. {
  7. typedef boost::tuple<std::string, int, bool> animal;
  8. animal a{"cat", 4, true};
  9. std::cout << std::boolalpha << a << '\n';
  10. }

Example 22.2 stores a name, the number of legs, and a flag that indicates whether the animal has a tail. All three values are placed in a tuple. When executed, this program displays (cat 4 true).

You can create a tuple using the helper function boost::make_tuple(), which works like the helper function std::make_pair() for std::pair (see Example 22.3).

Example 22.3. Creating tuples with boost::make_tuple()

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <iostream>
  4. int main()
  5. {
  6. std::cout.setf(std::ios::boolalpha);
  7. std::cout << boost::make_tuple("cat", 4, true) << '\n';
  8. }

A tuple can also contain references, as shown in Example 22.4.

Example 22.4. Tuples with references

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <boost/ref.hpp>
  4. #include <string>
  5. #include <iostream>
  6. int main()
  7. {
  8. std::string s = "cat";
  9. std::cout.setf(std::ios::boolalpha);
  10. std::cout << boost::make_tuple(boost::ref(s), 4, true) << '\n';
  11. }

The values 4 and true are passed by value and, thus, are stored directly inside the tuple, However, the first element is a reference to the string s. The function boost::ref() from Boost.Ref is used to create the reference. To create a constant reference, use boost::cref().

Usually, you can use std::ref() from the C++11 standard library instead of boost::ref(). However, Example 22.4 uses boost::ref() because only Boost.Ref provides an operator to write to standard output.

std::pair uses the member variables first and second to provide access. Because a tuple does not have a fixed number of elements, access must be handled differently.

Example 22.5. Reading elements of a tuple

  1. #include <boost/tuple/tuple.hpp>
  2. #include <string>
  3. #include <iostream>
  4. int main()
  5. {
  6. typedef boost::tuple<std::string, int, bool> animal;
  7. animal a = boost::make_tuple("cat", 4, true);
  8. std::cout << a.get<0>() << '\n';
  9. std::cout << boost::get<0>(a) << '\n';
  10. }

There are two ways to access values in a tuple. You can call the member function get(), or you can pass the tuple to the free-standing function boost::get(). In both cases, the index of the corresponding element in the tuple must be provided as a template parameter. Example 22.5 accesses the first element of the tuple a in both cases and, thus, displays cat twice.

Specifying an invalid index results in a compiler error because index validity is checked at compile time.

The member function get() and the free-standing function boost::get() both return a reference that allows you to change a value inside a tuple.

Example 22.6. Writing elements of a tuple

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <string>
  4. #include <iostream>
  5. int main()
  6. {
  7. typedef boost::tuple<std::string, int, bool> animal;
  8. animal a = boost::make_tuple("cat", 4, true);
  9. a.get<0>() = "dog";
  10. std::cout << std::boolalpha << a << '\n';
  11. }

Example 22.6 modifies the animal’s name and, thus, displays (dog 4 true).

Boost.Tuple also defines comparison operators. To compare tuples, include the header file boost/tuple/tuple_comparison.hpp.

Example 22.7. Comparing tuples

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_comparison.hpp>
  3. #include <string>
  4. #include <iostream>
  5. int main()
  6. {
  7. typedef boost::tuple<std::string, int, bool> animal;
  8. animal a1 = boost::make_tuple("cat", 4, true);
  9. animal a2 = boost::make_tuple("shark", 0, true);
  10. std::cout << std::boolalpha << (a1 != a2) << '\n';
  11. }

Example 22.7 displays true because the tuples a1 and a2 are different.

The header file boost/tuple/tuple_comparison.hpp also contains definitions for other comparison operators such as greater-than, which performs a lexicographical comparison.

Boost.Tuple supports a specific form of tuples called tier. Tiers are tuples whose elements are all reference types. They can be constructed with the function boost::tie().

Example 22.8. Creating a tier with boost::tie()

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <string>
  4. #include <iostream>
  5. int main()
  6. {
  7. typedef boost::tuple<std::string&, int&, bool&> animal;
  8. std::string name = "cat";
  9. int legs = 4;
  10. bool tail = true;
  11. animal a = boost::tie(name, legs, tail);
  12. name = "dog";
  13. std::cout << std::boolalpha << a << '\n';
  14. }

Example 22.8 creates a tier a, which consists of references to the variables name, legs, and tail. When the variable name is modified, the tier is modified at the same time.

Example 22.8 could have also been written using boost::make_tuple() and boost::ref() (see Example 22.9).

Example 22.9. Creating a tier without boost::tie()

  1. #include <boost/tuple/tuple.hpp>
  2. #include <boost/tuple/tuple_io.hpp>
  3. #include <string>
  4. #include <iostream>
  5. int main()
  6. {
  7. typedef boost::tuple<std::string&, int&, bool&> animal;
  8. std::string name = "cat";
  9. int legs = 4;
  10. bool tail = true;
  11. animal a = boost::make_tuple(boost::ref(name), boost::ref(legs),
  12. boost::ref(tail));
  13. name = "dog";
  14. std::cout << std::boolalpha << a << '\n';
  15. }

boost::tie() shortens the syntax. This function can also be used to unpack tuples. In Example 22.10, the individual values of the tuple, returned by a function, are instantly stored in variables.

Example 22.10. Unpacking return values of a function from a tuple

  1. #include <boost/tuple/tuple.hpp>
  2. #include <string>
  3. #include <iostream>
  4. boost::tuple<std::string, int> new_cat()
  5. {
  6. return boost::make_tuple("cat", 4);
  7. }
  8. int main()
  9. {
  10. std::string name;
  11. int legs;
  12. boost::tie(name, legs) = new_cat();
  13. std::cout << name << ", " << legs << '\n';
  14. }

boost::tie() stores the string “cat” and the number 4, both of which are returned as a tuple from newcat(), in the variables _name and legs.