14.3. Boost.Any

像 C++ 这样的强类型语言要求给每个变量一个确定的类型。 而以 JavaScript 为代表的弱类型语言却不这样做, 弱类型的每个变量都可以存储数组、 布尔值、 或者是字符串。

Boost.Any 给我们提供了 boost::any 类, 让我们可以在 C++ 中像 JavaScript 一样的使用弱类型的变量。

  1. #include <boost/any.hpp>
  2.  
  3. int main()
  4. {
  5. boost::any a = 1;
  6. a = 3.14;
  7. a = true;
  8. }

为了使用 boost::any, 你必须要包含头文件: boost/any.hpp。 接下来, 你就可以定义和使用 boost::any 的对象了。

需要注明的是: boost::any 并不能真的存储任意类型的值; Boost.Any 需要一些特定的前提条件才能工作。 任何想要存储在 boost::any 中的值,都必须是可拷贝构造的。 因此,想要在 boost::any 存储一个字符串类型的值, 就必须要用到 std::string , 就像在下面那个例子中做的一样。

  1. #include <boost/any.hpp>
  2. #include <string>
  3.  
  4. int main()
  5. {
  6. boost::any a = 1;
  7. a = 3.14;
  8. a = true;
  9. a = std::string("Hello, world!");
  10. }

如果你企图把字符串 "Hello, world!" 直接赋给 a , 你的编译器就会报错, 因为由基类型 char 构成的字符串在 C++ 中并不是可拷贝构造的。

想要访问 boost::any 中具体的内容, 你必须要使用转型操作: boost::any_cast

  1. #include <boost/any.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. boost::any a = 1;
  7. std::cout << boost::any_cast<int>(a) << std::endl;
  8. a = 3.14;
  9. std::cout << boost::any_cast<double>(a) << std::endl;
  10. a = true;
  11. std::cout << boost::any_cast<bool>(a) << std::endl;
  12. }

通过由模板参数传入 boost::any_cast 的值, 变量会被转化成相应的类型。 一旦你指定了一种非法的类型, 该操作会抛出 boost::bad_any_cast 类型的异常。

  1. #include <boost/any.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. try
  7. {
  8. boost::any a = 1;
  9. std::cout << boost::any_cast<float>(a) << std::endl;
  10. }
  11. catch (boost::bad_any_cast &e)
  12. {
  13. std::cerr << e.what() << std::endl;
  14. }
  15. }

上面的例子就抛出了一个异常, 因为 float 并不能匹配原本存储在 a 中的 int 类型。 记住, 在任何情况下都保证 boost::any 中的类型匹配是很重要的。 在没有通过模板参数指定 shortlong 类型时, 同样会有异常抛出。

既然 boost::bad_any_cast 继承自 std::bad_castcatch 当然也可以捕获相应类型的异常。

想要检查 boost::any 是否为空, 你可以使用 empty() 函数。 想要确定其中具体的类型信息, 你可以使用 type() 函数。

  1. #include <boost/any.hpp>
  2. #include <typeinfo>
  3. #include <iostream>
  4.  
  5. int main()
  6. {
  7. boost::any a = 1;
  8. if (!a.empty())
  9. {
  10. const std::type_info &ti = a.type();
  11. std::cout << ti.name() << std::endl;
  12. }
  13. }

上面的例子同时用到了 empty()type() 函数。 empty() 将会返回一个布尔值, 而 type() 则会返回一个在 typeinfo 中定义的 std::type_info 值。

作为对这一节的总结, 最后一个例子会向你展示怎样用 boost::any_cast 来定义一个指向 boost::any 中内容的指针。

  1. #include <boost/any.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. boost::any a = 1;
  7. int *i = boost::any_cast<int>(&a);
  8. std::cout << *i << std::endl;
  9. }

你需要做的就是传递一个 boost::any 类型的指针, 作为 boost::any_cast 的参数; 模板参数却没有任何改动。