• 14.4. Boost.Variant

    14.4. Boost.Variant

    Boost.Variant 和 Boost.Any 之间的不同点在于 Boost.Any 可以被视为任意的类型, 而 Boost.Variant 只能被视为固定数量的类型。 让我们来看下面这个例子。

    1. #include <boost/variant.hpp>
    2.  
    3. int main()
    4. {
    5. boost::variant<double, char> v;
    6. v = 3.14;
    7. v = 'A';
    8. }
    • 下载源代码

    Boost.Variant 为我们提供了一个定义在 boost/variant.hpp 中的类: boost::variant 。 既然 boost::variant 是一个模板, 你必须要指定至少一个参数。 Variant 所存储的数据类型就由这些参数来指定。 上面的例子就给 v 指定了 double 类型和 char 类型。 注意, 一旦你将一个 int 值赋给了 v, 你的代码将不会编译通过。

    当然, 上面的例子也可以用一个 union 类型来实现, 但是与 union 不同的是: boost::variant 可以储存像 std::string 这样的 class 类型的数据。

    1. #include <boost/variant.hpp>
    2. #include <string>
    3.  
    4. int main()
    5. {
    6. boost::variant<double, char, std::string> v;
    7. v = 3.14;
    8. v = 'A';
    9. v = "Hello, world!";
    10. }
    • 下载源代码

    要访问 v 中的数据, 你可以使用独立的 boost::get() 函数。

    1. #include <boost/variant.hpp>
    2. #include <string>
    3. #include <iostream>
    4.  
    5. int main()
    6. {
    7. boost::variant<double, char, std::string> v;
    8. v = 3.14;
    9. std::cout << boost::get<double>(v) << std::endl;
    10. v = 'A';
    11. std::cout << boost::get<char>(v) << std::endl;
    12. v = "Hello, world!";
    13. std::cout << boost::get<std::string>(v) << std::endl;
    14. }
    • 下载源代码

    boost::get() 需要传入一个模板参数来指明你需要返回的数据类型。 若是指定了一个非法的类型, 你会遇到一个运行时而不是编译期的错误。

    所有 boost::variant 类型的值都可以被直接写入标准输入流这样的流中, 这可以在一定程度上让你避开运行时错误的风险。

    1. #include <boost/variant.hpp>
    2. #include <string>
    3. #include <iostream>
    4.  
    5. int main()
    6. {
    7. boost::variant<double, char, std::string> v;
    8. v = 3.14;
    9. std::cout << v << std::endl;
    10. v = 'A';
    11. std::cout << v << std::endl;
    12. v = "Hello, world!";
    13. std::cout << v << std::endl;
    14. }
    • 下载源代码

    想要分别处理各种不同类型的数据, Boost.Variant 为我们提供了一个名为 boost::apply_visitor() 的函数。

    1. #include <boost/variant.hpp>
    2. #include <boost/any.hpp>
    3. #include <vector>
    4. #include <string>
    5. #include <iostream>
    6.  
    7. std::vector<boost::any> vector;
    8.  
    9. struct output :
    10. public boost::static_visitor<>
    11. {
    12. void operator()(double &d) const
    13. {
    14. vector.push_back(d);
    15. }
    16.  
    17. void operator()(char &c) const
    18. {
    19. vector.push_back(c);
    20. }
    21.  
    22. void operator()(std::string &s) const
    23. {
    24. vector.push_back(s);
    25. }
    26. };
    27.  
    28. int main()
    29. {
    30. boost::variant<double, char, std::string> v;
    31. v = 3.14;
    32. boost::apply_visitor(output(), v);
    33. v = 'A';
    34. boost::apply_visitor(output(), v);
    35. v = "Hello, world!";
    36. boost::apply_visitor(output(), v);
    37. }
    • 下载源代码

    boost::applyvisitor() 第一个参数需要传入一个继承自 boost::static_visitor 类型的对象。 这个类必须要重载 operator()() 运算符来处理 boost::variant 每个可能的类型。 相应的, 例子中的 _v 就重载了三次 operator() 来处理三种可能的类型: doublecharstd::string

    再仔细看代码, 不难发现 boost::static_visitor 是一个模板。 那么,当 operator()() 有返回值的时候, 就必须返回一个模板才行。 如果 operator() 像例子那样没有返回值时, 你就不需要模板了。

    boost::apply_visitor() 的第二个参数是一个 boost::variant 类型的值。

    在使用时, boost::apply_visitor() 会自动调用跟第二个参数匹配的 operator()() 。 示例程序中的 boost::apply_visitor() 就自动调用了三个不同的 operator 第一个是 double 类型的, 第二个是 char 最后一个是 std::string

    boost::apply_visitor() 的优点不只是“自动调用匹配的函数”这一点。 更有用的是, boost::apply_visitor() 会确认是否 boost::variant 中的每个可能值都定义了相应的函数。 如果你忘记重载了任何一个函数, 代码都不会编译通过。

    当然, 如果对每种类型的操作都是一样的, 你也可以像下面的示例一样使用一个模板来简化你的代码。

    1. #include <boost/variant.hpp>
    2. #include <boost/any.hpp>
    3. #include <vector>
    4. #include <string>
    5. #include <iostream>
    6.  
    7. std::vector<boost::any> vector;
    8.  
    9. struct output :
    10. public boost::static_visitor<>
    11. {
    12. template <typename T>
    13. void operator()(T &t) const
    14. {
    15. vector.push_back(t);
    16. }
    17. };
    18.  
    19. int main()
    20. {
    21. boost::variant<double, char, std::string> v;
    22. v = 3.14;
    23. boost::apply_visitor(output(), v);
    24. v = 'A';
    25. boost::apply_visitor(output(), v);
    26. v = "Hello, world!";
    27. boost::apply_visitor(output(), v);
    28. }
    • 下载源代码

    既然 boost::apply_visitor() 可以在编译期确定代码的正确性, 你就该更多的使用它而不是 boost::get()