14.2. 元组
Boost.Tuple 库提供了一个更一般的版本的 std::pair
—— boost::tuple
。 不过 std::pair
只能储存两个值而已, boost::tuple
则给了我们更多的选择。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string, std::string> person;
- person p("Boris", "Schaeling");
- std::cout << p << std::endl;
- }
为了使用 boost::tuple
, 你必须要包含头文件: boost/tuple/tuple.hpp
。 若想要让元组和流一起使用, 你还需要包含头文件: boost/tuple/tuple_io.hpp
才行。
其实, boost::tuple
的用法基本上和 std::pair
一样。 就像我们在上面的例子里看到的那样, 两个值类型的 std::string
通过两个相应的模板参数存储在了元组里。
当然 person
类型也可以用 std::pair
来实现。 所有 boost::tuple
类型的对象都可以被写入流里。 再次强调, 为了使用流操作和各种流操作运算符, 你必须要包含头文件: boost/tuple/tuple_io.hpp
。 显然,我们的例子会输出: (Boris Schaeling)
。
boost::tuple
和 std::pair
之间最重要的一点不同点: 元组可以存储无限多个值!
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string, std::string, int> person;
- person p("Boris", "Schaeling", 43);
- std::cout << p << std::endl;
- }
我们修改了实例, 现在的元组里不仅储存了一个人的firstname和lastname, 还加上了他的鞋子的尺码。 现在, 我们的例子将会输出: (Boris Schaeling 43)
。
就像 std::pair
有辅助函数 std::make_pair()
一样, 一个元组也可以用它的辅助函数 boost::make_tuple()
来创建。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <iostream>
- int main()
- {
- std::cout << boost::make_tuple("Boris", "Schaeling", 43) << std::endl;
- }
就像下面的例子所演示的那样, 一个元组也可以存储引用类型的值。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- std::string s = "Boris";
- std::cout << boost::make_tuple(boost::ref(s), "Schaeling", 43) << std::endl;
- }
因为 "Schaeling" 和 43 是按值传递的,所以就直接存储在了元组中。 与他们不同的是: person 的第一个元素是一个指向 s 的引用。 Boost.Ref 中的 boost::ref()
就是用来创建这样的引用的。 相对的, 要创建一个常量的引用的时候, 你需要使用 boost::cref()
。
在学习了创建元组的方法之后, 让我们来了解一下访问元组中元素的方式。 std::pair
只包含两个元素, 故可以使用属性 first 和 second 来访问其中的元素。 但元组可以包含无限多个元素, 显然, 我们需要用另一种方式来解决访问的问题。
- #include <boost/tuple/tuple.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string, std::string, int> person;
- person p = boost::make_tuple("Boris", "Schaeling", 43);
- std::cout << p.get<0>() << std::endl;
- std::cout << boost::get<0>(p) << std::endl;
- }
我们可以用两种方式来访问元组中的元素: 使用成员函数 get()
, 或者将元组传给一个独立的函数 boost::get()
。 使用这两种方式时, 元素的索引值都是通过模板参数来指定的。 例子中就分别使用了这两种方式来访问 p 中的第一个元素。 因此, Boris
会被输出两次。
另外, 对于索引值合法性的检查会在编译期执行, 故访问非法的索引值会引起编译期错误而不是运行时的错误。
对于元组中元素的修改, 你同样可以使用 get()
和 boost::get()
函数。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string, std::string, int> person;
- person p = boost::make_tuple("Boris", "Schaeling", 43);
- p.get<1>() = "Becker";
- std::cout << p << std::endl;
- }
get()
和 boost::get()
都会返回一个引用值。 例子中修改了 lastname 之后将会输出: (Boris Becker 43)
。
Boost.Tuple 除了重载了流操作运算符以外, 还为我们提供了比较运算符。 为了使用它们, 你必须要包含相应的头文件: boost/tuple/tuple_comparison.hpp
。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_comparison.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string, std::string, int> person;
- person p1 = boost::make_tuple("Boris", "Schaeling", 43);
- person p2 = boost::make_tuple("Boris", "Becker", 43);
- std::cout << (p1 != p2) << std::endl;
- }
上面的例子将会输出 1
因为两个元组 p1 和 p2 是不同的。
同时, 头文件 boost/tuple/tuple_comparison.hpp
还定义了一些其他的比较操作, 比如用来做字典序比较的大于操作等。
Boost.Tuple 还提供了一种叫做 Tier 的特殊元组。 Tier 的特殊之处在于它包含的所有元素都是引用类型的。 它可以通过构造函数 boost::tie()
来创建。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string&, std::string&, int&> person;
- std::string firstname = "Boris";
- std::string surname = "Schaeling";
- int shoesize = 43;
- person p = boost::tie(firstname, surname, shoesize);
- surname = "Becker";
- std::cout << p << std::endl;
- }
上面的例子创建了一个 tier p, 他包含了三个分别指向 firstname, surname 和 shoesize 的引用值。 在修改变量 surname 的同时, tier 也会跟着改变。
就像下面的例子展示的那样,你当然可以用 boost::make_tuple()
和 boost::ref()
来代替构造函数 boost::tie()
。
- #include <boost/tuple/tuple.hpp>
- #include <boost/tuple/tuple_io.hpp>
- #include <string>
- #include <iostream>
- int main()
- {
- typedef boost::tuple<std::string&, std::string&, int&> person;
- std::string firstname = "Boris";
- std::string surname = "Schaeling";
- int shoesize = 43;
- person p = boost::make_tuple(boost::ref(firstname), boost::ref(surname), boost::ref(shoesize));
- surname = "Becker";
- std::cout << p << std::endl;
- }
boost::tie()
在一定程度上简化了语法, 同时, 也可以用作“拆箱”元组。 在接下来的这个例子里, 元组中的各个元素就被很方便的“拆箱”并直接赋给了其他变量。
- #include <boost/tuple/tuple.hpp>
- #include <string>
- #include <iostream>
- boost::tuple<std::string, int> func()
- {
- return boost::make_tuple("Error message", 2009);
- }
- int main()
- {
- std::string errmsg;
- int errcode;
- boost::tie(errmsg, errcode) = func();
- std::cout << errmsg << ": " << errcode << std::endl;
- }
通过使用 boost::tie()
, 元组中的元素:字符串“Error massage”和错误代码“2009”就很方便地经 func()
的返回值直接赋给了 errmsg 和 errcode 。