• 14.2. 元组

    14.2. 元组

    Boost.Tuple 库提供了一个更一般的版本的 std::pair —— boost::tuple 。 不过 std::pair 只能储存两个值而已, boost::tuple 则给了我们更多的选择。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. typedef boost::tuple<std::string, std::string> person;
    9. person p("Boris", "Schaeling");
    10. std::cout << p << std::endl;
    11. }
    • 下载源代码

    为了使用 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::tuplestd::pair 之间最重要的一点不同点: 元组可以存储无限多个值!

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. typedef boost::tuple<std::string, std::string, int> person;
    9. person p("Boris", "Schaeling", 43);
    10. std::cout << p << std::endl;
    11. }
    • 下载源代码

    我们修改了实例, 现在的元组里不仅储存了一个人的firstname和lastname, 还加上了他的鞋子的尺码。 现在, 我们的例子将会输出: (Boris Schaeling 43)

    就像 std::pair 有辅助函数 std::make_pair() 一样, 一个元组也可以用它的辅助函数 boost::make_tuple() 来创建。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <iostream>
    4.  
    5. int main()
    6. {
    7. std::cout << boost::make_tuple("Boris", "Schaeling", 43) << std::endl;
    8. }
    • 下载源代码

    就像下面的例子所演示的那样, 一个元组也可以存储引用类型的值。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. std::string s = "Boris";
    9. std::cout << boost::make_tuple(boost::ref(s), "Schaeling", 43) << std::endl;
    10. }
    • 下载源代码

    因为 "Schaeling" 和 43 是按值传递的,所以就直接存储在了元组中。 与他们不同的是: person 的第一个元素是一个指向 s 的引用。 Boost.Ref 中的 boost::ref() 就是用来创建这样的引用的。 相对的, 要创建一个常量的引用的时候, 你需要使用 boost::cref()

    在学习了创建元组的方法之后, 让我们来了解一下访问元组中元素的方式。 std::pair 只包含两个元素, 故可以使用属性 firstsecond 来访问其中的元素。 但元组可以包含无限多个元素, 显然, 我们需要用另一种方式来解决访问的问题。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <string>
    3. #include <iostream>
    4.  
    5. int main()
    6. {
    7. typedef boost::tuple<std::string, std::string, int> person;
    8. person p = boost::make_tuple("Boris", "Schaeling", 43);
    9. std::cout << p.get<0>() << std::endl;
    10. std::cout << boost::get<0>(p) << std::endl;
    11. }
    • 下载源代码

    我们可以用两种方式来访问元组中的元素: 使用成员函数 get() , 或者将元组传给一个独立的函数 boost::get() 。 使用这两种方式时, 元素的索引值都是通过模板参数来指定的。 例子中就分别使用了这两种方式来访问 p 中的第一个元素。 因此, Boris 会被输出两次。

    另外, 对于索引值合法性的检查会在编译期执行, 故访问非法的索引值会引起编译期错误而不是运行时的错误。

    对于元组中元素的修改, 你同样可以使用 get()boost::get() 函数。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. typedef boost::tuple<std::string, std::string, int> person;
    9. person p = boost::make_tuple("Boris", "Schaeling", 43);
    10. p.get<1>() = "Becker";
    11. std::cout << p << std::endl;
    12. }
    • 下载源代码

    get()boost::get() 都会返回一个引用值。 例子中修改了 lastname 之后将会输出: (Boris Becker 43)

    Boost.Tuple 除了重载了流操作运算符以外, 还为我们提供了比较运算符。 为了使用它们, 你必须要包含相应的头文件: boost/tuple/tuple_comparison.hpp

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_comparison.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. typedef boost::tuple<std::string, std::string, int> person;
    9. person p1 = boost::make_tuple("Boris", "Schaeling", 43);
    10. person p2 = boost::make_tuple("Boris", "Becker", 43);
    11. std::cout << (p1 != p2) << std::endl;
    12. }
    • 下载源代码

    上面的例子将会输出 1 因为两个元组 p1p2 是不同的。

    同时, 头文件 boost/tuple/tuple_comparison.hpp 还定义了一些其他的比较操作, 比如用来做字典序比较的大于操作等。

    Boost.Tuple 还提供了一种叫做 Tier 的特殊元组。 Tier 的特殊之处在于它包含的所有元素都是引用类型的。 它可以通过构造函数 boost::tie() 来创建。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. typedef boost::tuple<std::string&, std::string&, int&> person;
    9.  
    10. std::string firstname = "Boris";
    11. std::string surname = "Schaeling";
    12. int shoesize = 43;
    13. person p = boost::tie(firstname, surname, shoesize);
    14. surname = "Becker";
    15. std::cout << p << std::endl;
    16. }
    • 下载源代码

    上面的例子创建了一个 tier p, 他包含了三个分别指向 firstnamesurnameshoesize 的引用值。 在修改变量 surname 的同时, tier 也会跟着改变。

    就像下面的例子展示的那样,你当然可以用 boost::make_tuple()boost::ref() 来代替构造函数 boost::tie()

    1. #include <boost/tuple/tuple.hpp>
    2. #include <boost/tuple/tuple_io.hpp>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. int main()
    7. {
    8. typedef boost::tuple<std::string&, std::string&, int&> person;
    9.  
    10. std::string firstname = "Boris";
    11. std::string surname = "Schaeling";
    12. int shoesize = 43;
    13. person p = boost::make_tuple(boost::ref(firstname), boost::ref(surname), boost::ref(shoesize));
    14. surname = "Becker";
    15. std::cout << p << std::endl;
    16. }
    • 下载源代码

    boost::tie() 在一定程度上简化了语法, 同时, 也可以用作“拆箱”元组。 在接下来的这个例子里, 元组中的各个元素就被很方便的“拆箱”并直接赋给了其他变量。

    1. #include <boost/tuple/tuple.hpp>
    2. #include <string>
    3. #include <iostream>
    4.  
    5. boost::tuple<std::string, int> func()
    6. {
    7. return boost::make_tuple("Error message", 2009);
    8. }
    9.  
    10. int main()
    11. {
    12. std::string errmsg;
    13. int errcode;
    14.  
    15. boost::tie(errmsg, errcode) = func();
    16. std::cout << errmsg << ": " << errcode << std::endl;
    17. }
    • 下载源代码

    通过使用 boost::tie() , 元组中的元素:字符串“Error massage”和错误代码“2009”就很方便地经 func() 的返回值直接赋给了 errmsgerrcode