Boost.Any
Any库支持类型安全地存储和获取任意类型的值。当你需要一个可变的类型时,有三种可能的解决方案:
- 无限制的类型,如 void*. 这种方法不可能是类型安全的,应该象逃避灾难一样避免它。
- 可变的类型,即支持多种类型的存储和获取的类型。
- 支持转换的类型,如字符串类型与整数类型之间的转换。
Any实现了第二种方案,一个基于值的可变化的类型,无限可能的类型。这个库通常用于把不同类型的东西存储到标准库的容器中。
Any 库如何改进你的程序?
- 任意类型的类型安全存储以及安全的取回
- 在标准库容器中存放不同类型的方法
- 可以在无须知道类型的情况下传送类型
Any库提供一个类型, any, 它允许存入任意类型且稍后取回,而不损失类型安全性。它有点象是可变类型的化合物:它可以持有任意类型,但你必须知道类型才能取回值。有很多次你想在同一个容器中存入互不相关的类型。有很多次某些代码只想从一个指针向另一个指针传送数据,而不关心数据的类型。从表面看,这些事情很容易做。它们可以通过一个无类的类型来实现,如 void*. 它们也可以通过使用一个含有不同类型的union来实现。有很多可变类型通过一些类型标识机制来实现。不幸的是,所有这些都缺乏类型安全性,而只有在最可控的情形下我们才应该故意绕过类型系统。标准库的容器是要通过它们所包含的类型来特化的,这意味着不可能把不同类型的元素存入容器之内。幸运的是,解决的方案不一定要 void*, 因为 Any 库允许你将存入不同的类型而稍后取回。没有办法在不知道实际类型的情况下取回存入的值,类型安全从而得到保证。
在设计框架时,不可能预先知道哪些类型要和框架类一起使用。一个常见的方法是,要求框架的使用者遵守某种接口,或者从框架所提供的某个基类进行派生。这是合理的,因为框架可能需要与不同的高级类进行通信才能使用。但是也存在这样的情形,框架对于存入或接受的类型无须(或不能)知道任何相关信息。不要绕过类型系统去使用 void* 方法,框架可以使用 any 。
Any 如何适用于标准库?
Any的一个重要特性是,它提供了存储不同类型的对象到标准库容器中的能力。它也是一种可变数据类型,这正是C++标准库非常需要而又缺乏的。
Boost.Any
头文件: "boost/any.hpp"
类 any 允许对任意类型进行类型安全的存储和取回。不象无类类型,any 保存了类型信息,并且不会让你在不知道正确类型的情况下获得存入的值。当然,有办法可以让你询问关于类型的信息,也有测试保存的值的方法,但最终,调用者必须知道在 any 对象中的值的真实类型,否则不能访问any。可以把 any 看作为上锁的安全性。没有正确的钥匙,你不能进入其中。any 对它所保存的类型有以下要求:
- CopyConstructible 它必须可以复制这个类型
- Non-throwing destructor 就象所有析构函数应该的那样!
- Assignable 为了保证强异常安全(不符合可赋值要求的类型也可以用于 any, 但没有强异常安全的保证)
以下是 any 的公有接口:
namespace boost {
class any {
public:
any();
any(const any&);
template
any(const ValueType&);
~any();
any& swap(any &);
any& operator=(const any&);
template
any& operator=(const ValueType&);
bool empty() const;
const std::type_info& type() const;
};
}
成员函数
缺省构造函数创建一个空的 any 实例,即一个不含有值的 any。当然,你无法从一个空的any中取回值,因为没有值存在。
创建一个已有 any 对象的独立拷贝。other 中含有的值被复制并存入 this.
- template any(const ValueType&);
这个模板构造函数存入一个传入的ValueType类型参数的拷贝。参数是一个 const 引用,因此传入一个临时对象来存入any是合法的。注意,该构造函数不是 explicit 的,如果是的话, any 会难以使用,而且也不会增加安全性。
析构函数销毁含有的值,但注意,由于对一个裸指针的析构不会调用operator delete 或 operator delete[] ,所以在any中使用指针时,你应该把裸指针包装成一个象 shared_ptr 那样的智能指针。
交换存在两个 any 对象中的值。
- any& operator=(const any& other);
如果any实例非空,则丢弃所存放的值,并存入other值的拷贝。
- template
- any& operator=(const ValueType& value);
如果any实例非空,则丢弃所存放的值,并存入 value 的一份拷贝,value可以是任意符合any要求的类型。
给出any实例当前是否有值,不管是什么值。因而,当any持有一个指针时,即使该指针值为空,则 empty 也返回 false 。
- const std::type_info& type() const;
给出所存值的类型。如果 any 为空,则类型为 void.
普通函数
- template
- ValueType any_cast(const any& operand);
any_cast 让你访问any中存放的值。参数为需要取回值的 any 对象。如果类型 ValueType 与所存值不符,any 抛出一个 bad_any_cast 异常。请注意,这个语法有点象 dynamic_cast.
- template
- const ValueType* any_cast(const any* operand);
any_cast 的一个重载,接受一个指向 any 的指针,并返回一个指向所存值的指针。如果 any 中的类型不是 ValueType, 则返回一个空指针。请再次注意,这个语法也有点象 dynamic_cast.
- template
- ValueType* any_cast(any* operand);
any_cast 的另一个重载,与前一个版本相似,但前一个版本使用 const 指针的参数并返回 const 指针,这个版本则不是。
异常
bad_any_cast
当试图将一个any对象转换为该对象所存类型以外的其它类型,将抛出该异常。bad_any_cast 派生自 std::bad_cast. 注意,使用指针参数调用 any_cast 时,将不抛出异常(类似于对指针使用 dynamic_cast 时返回空指针一样),反之对引用类型使用 dynamic_cast 则会在失败时抛出异常。
用法
Any库定义在名字空间 boost 内。你要用类 any 来保存值,用模板函数 any_cast 来取回存放的值。为了使用 any, 要包含头文件 "boost/any.hpp". 创建一个可以存放任意值的实例是很容易的。
把任意类型的值赋给它也很容易。
a=std::string("A string");
a=42;
a=3.1415;
any几乎可以接受任何东西!但是,为了真正能使用存放在any中的值,我们需要取回它,对吧?为此,我们需要知道这个值的类型。
- std::string s=boost::any_cast(a);
- // 抛出 boost::bad_any_cast.
这显然不行;因为当前的 a 所持的是一个 double, any_cast 抛出一个 bad_any_cast 异常。以下这样则可以。
- double d=boost::any_cast(a);
any 只允许你在知道类型的前提下访问它的值,这是很明智的。对于这个库,典型情况下你只需记住两件事:类 any, 用于存放值,还有模板函数 any_cast, 用于取回值。
任意的东西!
考虑三个类,A, B, 和 C, 它们没有共同的基类,而我们想把它们存入一个 std::vector. 如果它们没有共同基类,看起来我们不得不把它们当成 void* 来保存,对吗?唔,not any more (相关语,没有更多的了),因为类型 any 没有改变对所存值的类型的依赖。以下代码示范了如何解决这个问题。
#include
#include
#include
#include
#include "boost/any.hpp"
class A {
public:
void some_function() { std::cout << "A::some_function()\n"; }
};
class B {
public:
void some_function() { std::cout << "B::some_function()\n"; }
};
class C {
public:
void some_function() { std::cout << "C::some_function()\n"; }
};
int main() {
std::cout << "Example of using any.\n\n";
std::vector store_anything;
store_anything.push_back