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<typename ValueType>
- any(const ValueType&);
- ~any();
- any& swap(any &);
- any& operator=(const any&);
- template<typename ValueType>
- any& operator=(const ValueType&);
- bool empty() const;
- const std::type_info& type() const;
- };
- }
- any();
- any(const any& other);
- template<typename ValueType> any(const ValueType&);
- ~any();
- any& swap(any& other);
- any& operator=(const any& other);
- template<typename ValueType>
- any& operator=(const ValueType& value);
- bool empty() const;
- const std::type_info& type() const;
普通函数
- template<typename ValueType>
- ValueType any_cast(const any& operand);
- template<typename ValueType>
- const ValueType* any_cast(const any* operand);
- template<typename ValueType>
- ValueType* any_cast(any* operand);
异常
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". 创建一个可以存放任意值的实例是很容易的。
- boost::any a;
- a=std::string("A string");
- a=42;
- a=3.1415;
- std::string s=boost::any_cast<std::string>(a);
- // 抛出 boost::bad_any_cast.
- double d=boost::any_cast<double>(a);
任意的东西!
考虑三个类,A, B, 和 C, 它们没有共同的基类,而我们想把它们存入一个 std::vector. 如果它们没有共同基类,看起来我们不得不把它们当成 void* 来保存,对吗?唔,not any more (相关语,没有更多的了),因为类型 any 没有改变对所存值的类型的依赖。以下代码示范了如何解决这个问题。
- #include <iostream>
- #include <string>
- #include <utility>
- #include <vector>
- #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<boost::any> store_anything;
- store_anything.push_back(A());
- store_anything.push_back(B());
- store_anything.push_back(C());
- // 我们再来,再加一些别的东西
- store_anything.push_back(std::string("This is fantastic! "));
- store_anything.push_back(3);
- store_anything.push_back(std::make_pair(true, 7.92));
- void print_any(boost::any& a);
- // 稍后定义;打印a中的值
- std::for_each(
- store_anything.begin(),
- store_anything.end(),
- print_any);
- }
Example of using any.
A::some_function()
B::some_function()
C::some_function()
string: This is fantastic!
Oops!
Oops!
好的,我们可以保存任意我们想要的东西,但我们如何取回保存在vector的元素中的值呢?在前例中,我们用 for_each 来为vector中的每个元素调用 print_any() 。
- void print_any(boost::any& a) {
- if (A* pA=boost::any_cast<A>(&a)) {
- pA->some_function();
- }
- else if (B* pB=boost::any_cast<B>(&a)) {
- pB->some_function();
- }
- else if (C* pC=boost::any_cast<C>(&a)) {
- pC->some_function();
- }
- }
- else {
- try {
- std::cout << boost::any_cast<std::string>(a) << '\n';
- }
- catch(boost::bad_any_cast&) {
- std::cout << "Oops!\n";
- }
- }
- }
使用 any 让你能够在原来不能使用标准库容器和算法的地方下使用它们,从而让你可以写出更具有可维护性和更易懂的代码。
属性类
现在我们想定义一个可用于容器的属性类。我们将以字符串方式来保存属性的名字,而属性值则可以为任意类型。虽然我们可以要求所有属性值从一个共同的基类派生而来,但这样常常不可行。例如,我们可能无法访问所有那些我们需要用作属性值的类的源代码,也有可能有些属性值是内建类型,不可能是派生类(此外,那样也不能做出一个好的 any 示例)。通过把属性值存入一个 any 实例,我们可以让使用者去处理那些他们知道类型且感兴趣的属性值。
- #include <iostream>
- #include <string>
- #include <vector>
- #include <algorithm>
- #include "boost/any.hpp"
- class property {
- boost::any value_;
- std::string name_;
- public:
- property(
- const std::string& name,
- const boost::any& value)
- : name_(name),value_(value) {}
- std::string name() const { return name_; }
- boost::any& value() { return value_; }
- friend bool operator<
- (const property& lhs, const property& rhs) {
- return lhs.name_<rhs.name_;
- }
- };
以下程序使用了我们新的、灵活的property类,全亏了 any!property 类的实例被存入一个 std::vector 并以属性名排序(译注:原文是说存入一个 std::map,似有误)。
- void print_names(const property& p) {
- std::cout << p.name() << "\n";
- }
- int main() {
- std::cout << "Example of using any for storing properties.\n";
- std::vector<property> properties;
- properties.push_back(
- property("B", 30));
- properties.push_back(
- property("A", std::string("Thirty something")));
- properties.push_back(property("C", 3.1415));
- std::sort(properties.begin(),properties.end());
- std::for_each(
- properties.begin(),
- properties.end(),
- print_names);
- std::cout << "\n";
- std::cout <<
- boost::any_cast<std::string>(properties[0].value()) << "\n";
- std::cout <<
- boost::any_cast<int>(properties[1].value()) << "\n";
- std::cout <<
- boost::any_cast<double>(properties[2].value()) << "\n";
- }
Example of using any for storing properties.
A
B
C
Thirty something
30
3.1415
在这个例子中,由于容器被排序了,我们可以按索引取回属性值,而且我们预先知道它们各自的类型,所以我们不需要用 try/catch 块来处理取回操作。从一个 any 实例中取回值时,如果失败表示一个真正的错误,则应该用 const 引用来传递 any 。
- std::string s=boost::any_cast<std::string>(a);
- std::string* ps=boost::any_cast<std::string>(&a);
如果值的类型在拷贝时代价很昂贵,就应该传递指针以避免值的拷贝。
关于 any 的更多
any 还提供了其它几个成员函数,如测试一个 any 实例是否为空,交换两个 any 实例的值。以下例子示范了如何使用它们。
- #include <iostream>
- #include <string>
- #include "boost/any.hpp"
- int main() {
- std::cout << "Example of using any member functions\n\n";
- boost::any a1(100);
- boost::any a2(std::string("200"));
- boost::any a3;
- std::cout << "a3 is ";
- if (!a3.empty()) {
- std::cout << "not ";
- }
- std::cout << "empty\n";
- a1.swap(a2);
- try {
- std::string s=boost::any_cast<std::string>(a1);
- std::cout << "a1 contains a string: " << s << "\n";
- }
- catch(boost::bad_any_cast& e) {
- std::cout << "I guess a1 doesn't contain a string!\n";
- }
- if (int* p=boost::any_cast<int>(&a2)) {
- std::cout << "a2 seems to have swapped contents with a1: "
- << *p << "\n";
- }
- else {
- std::cout << "Nope, no int in a2\n";
- }
- if (typeid(int)==a2.type()) {
- std::cout << "a2's type_info equals the type_info of int\n";
- }
- }
Example of using any member functions
a3 is empty
a1 contains a string: 200
a2 seems to have swapped contents with a1: 100
a2's type_info equals the type_info of int
让我们来更进一步分析这段代码。为了测试一个 any 实例是否包含值,我们调用成员函数 empty. 我们这样来测试 any a3 。
- std::cout << "a3 is ";
- if (!a3.empty()) {
- std::cout << "not ";
- }
- std::cout << "empty\n";
- a1.swap(a2);
- if (typeid(int)==a2.type()) {
在any中保存指针
通常,测试 empty 足以知道对象是否真的包含有效的东西。但是,如果 any 持有的是一个指针,则要在解引用它之前额外小心地测试这个指针。仅仅简单地测试 any 是否为空是不够的,因为一个 any 在持有一个指针时会被认为是非空的,即使这个指针是空的。
- boost::any a(static_cast<std::string*>(0));
- if (!a.empty()) {
- try {
- std::string* p=boost::any_cast<std::string*>(a);
- if (p) {
- std::cout << *p;
- }
- else {
- std::cout << "The any contained a null pointer!\n";
- }
- }
- catch(boost::bad_any_cast&) {}
- }
在 any 中保存裸指针的另一个麻烦在于析构的语义。any 类接受了它所存值的所有权,因为它保持一个该值的内部拷贝,并与 any 一起销毁它。但是,销毁一个裸指针并不会对它调用 delete 或 delete[] !它仅仅要求归还属于指针的那点内存。这使得在 any 中保存指针是有问题的,所以更好的办法是使用智能指针来代替。的确,使用智能指针是在一个 any 中保存指针的好办法。它解决了要保证所存指针指向的内存被正确释放的问题。当智能指针被销毁时,它会正确地确保内存及其中的数据被正确销毁。作为对比,要注意 std::auto_ptr 不是合适的智能指针。这是因为 auto_ptr 没有通常的复制语义;访问一个 any 中的值会把内存及其中数据的所有权从 any 转移到返回的 auto_ptr 中。
考虑以下代码。
- #include <iostream>
- #include <string>
- #include <algorithm>
- #include <vector>
- #include "boost/any.hpp"
- #include "boost/shared_ptr.hpp"
- class A {
- public:
- virtual ~A() {
- std::cout << "A::~A()\n";
- }
- void not_virtual() {
- std::cout << "A::not_virtual()\n";
- }
- virtual void is_virtual () {
- std::cout << "A:: is_virtual ()\n";
- }
- };
- class B : public A {
- public:
- void not_virtual() {
- std::cout << "B::not_virtual()\n";
- }
- virtual void is_virtual () {
- std::cout << "B:: is_virtual ()\n";
- }
- };
- void foo(boost::any& a) {
- std::cout << "\n";
- // 试一下 boost::shared_ptr<A>
- try {
- boost::shared_ptr<A> ptr=
- boost::any_cast<boost::shared_ptr<A> >(a);
- std::cout << "This any contained a boost::shared_ptr<A>\n";
- ptr-> is_virtual ();
- ptr->not_virtual();
- return;
- }
- catch(boost::bad_any_cast& e) {}
- // 试一下 boost::shared_ptr<B>
- try {
- boost::shared_ptr<B> ptr=
- boost::any_cast<boost::shared_ptr<B> >(a);
- std::cout << "This any contained a boost::shared_ptr<B>\n";
- ptr-> is_virtual ();
- ptr->not_virtual();
- return;
- }
- catch(boost::bad_any_cast& e) {}
- // 如果是其它东西(如一个字符串), 则忽略它
- std::cout <<
- "The any didn't contain anything that \
- concerns this function!\n";
- }
- int main() {
- std::cout << "Example of any and shared_ptr\n";
- boost::any a1(boost::shared_ptr<A>(new A));
- boost::any a2(std::string("Just a string"));
- {
- boost::any b1(boost::shared_ptr<A>(new B));
- boost::any b2(boost::shared_ptr<B>(new B));
- std::vector<boost::any> vec;
- vec.push_back(a1);
- vec.push_back(a2);
- vec.push_back(b1);
- vec.push_back(b2);
- std::for_each(vec.begin(),vec.end(),foo);
- std::cout << "\n";
- }
- std::cout <<
- "any's b1 and b2 have been destroyed which means\n"
- "that the shared_ptrs' reference counts became zero\n";
- }
Example of any and shared_ptr
This any contained a boost::shared_ptr<A>
A:: is_virtual ()
A::not_virtual()
The any didn't contain anything that concerns this function!
This any contained a boost::shared_ptr<A>
B:: is_virtual ()
A::not_virtual()
This any contained a boost::shared_ptr<B>
B:: is_virtual ()
B::not_virtual()
A::~A()
A::~A()
any's b1 and b2 have been destroyed which means
that the shared_ptrs' reference counts became zero
A::~A()
首先,我们看到传给 foo 的 any 含有一个 shared_ptr<A>, 它恰好拥有一个 A 的实例。输出正是我们所期望的。
接着是我们加到 vector 中的含有 string 的 any 。这显示了保存一些对稍后要被调用的函数而言是未知类型的类型到一个 any,是很有可能的,通常也是合理的;这些函数只需要处理它们需要操作的类型!
接下来的事情更有趣了,第三个元素含有一个指向 B 实例的 shared_ptr<A> 。这是一个例子,说明了 any 如何与其它类型一样实现多态性。当然,如果我们使用裸指针,就需要用 static_cast 来保存指针为我们想标识的类型。注意,函数 A::not_virtual 被调用而不是 B::not_virtual. 原原因是这个指针的静态类型是 A*, 而不是 B*.
最后一个元素含有一个 shared_ptr<B> ,它也正好指向一个 B 的实例。再一次,我们可以控制保存在 any 的类型,在后面一个 try 中设置相应的参数来打开它。
在里面的那个作用域结束时,vector 被销毁,它又销毁了内含的 any 实例,后者再销毁所有的 shared_ptr,正确地设置引用参数为零。接着,我们的指针被安全和不费力气地销毁!
这个例子显示了一些比如何与 any 一起使用智能指针更为重要的东西;它(又一次)显示了存入 any 的类型有是简单的或是复杂的都无关紧要。如果复制被存值的代价是高得惊人的,或者如果需要共享使用和控制生存期,就应该考虑使用智能指针,就象使用标准库的容器保存值一样。同样的推理也适用于 any, 通常这两个原则是一致的,正如在容器中使用 any 就意味着要保存不同的类型。
输入和输出操作符怎么啦?
any 用户的一个常见问题是,"为什么没有输入和输出操作符?" 这真的是有原因的。让我们从输入操作符开始。输入的语义应该是什么?它应该缺省为一个 string 类型吗?any当前持有的类型应该被用于从流中读取数据吗?如果是的话,那么首先为什么要用 any 呢?这些问题没有好的答案,这正是为什么 any 没有输入操作符的原因。回答第二个问题并不容易,但差不多。让 any 支持一个输出操作符意味着 any 不再能够保存任意的类型,因为这个操作符对于保存在 any 中的类型增加了一个要求。如果我们不有意去使用operator<<,这本无关紧要;但一个含有不支持输出操作符的类型的 any 实例仍是非法的,在编译时会导致一个错误。当然,只要我们提供一个模板版本的 operator<<, 我们就可以使用 any 而无须要求被包含的类型支持流输出,但一旦这个操作符被实例化,这个要求还是会被打开。
看起来,这些就是没有这些操作符的原因了,对吗?如果我们给可以匹配任意东西的 any 提供一个有效的输出操作符,并把 operator<< 引入到只能从 any 类的实现细节进行访问的作用域,它会是什么样的呢?那样的话,我们可以在执行输出到一个流时选择抛出一个异常或返回一个错误代码(这个功能仅用于那些不支持 operator<< 的参数),我们将要在运行期去做这些动作,而不影响其它代码的合法性。这种想法非常吸引我,我用几个手边的编译器上试了一下。结果不太好。我不想详细讨论它,但简而言之,这种方法需要一些很多编译器目前还不能处理的技术。但是,我们无需修改 any 类,我们可以创建一个利用 any 来保存任意类型的新类,并让这个新类支持 operator<<. 基本上,我们无论如何都需要做的是,any 要了解被含类型,知道如何进行输出,然后加到输出流中去。
增加对输出的支持——any_out
我们将定义一个类,它具有通过 operator<< 进行输出的功能。这增加了对被存类型的要求;作为可以保存在类 any_out 中的有效类型,必须要支持 operator<<.
- #include <iostream>
- #include <vector>
- #include <string>
- #include <ostream>
- #include "boost/any.hpp"
- class any_out {
- boost::any o_;
- struct streamer {
- virtual void print(std::ostream& o,boost::any& a)=0;
- virtual streamer* clone()=0;
- virtual ~streamer() {}
- };
- template <typename T> struct streamer_imp : public streamer {
- virtual void print(std::ostream& o,boost::any& a) {
- o << *boost::any_cast<T>(&a);
- }
- virtual streamer* clone() {
- return new streamer_imp<T>();
- }
- };
- class any_out {
- streamer* streamer_;
- boost::any o_;
- public:
- any_out() : streamer_(0) {}
- template <typename T> any_out(const T& value) :
- streamer_(new streamer_imp<T>),o_(value) {}
- any_out(const any_out& a)
- : streamer_(a.streamer_?a.streamer_->clone():0),o_(a.o_) {}[1]
- template<typename T> any_out& operator=(const T& r) {
- any_out(r).swap(*this);
- return *this;
- }
- any_out& operator=(const any_out& r) {
- any_out(r).swap(*this);
- return *this;
- }
- ~any_out() {
- delete streamer_;
- }
- any_out& swap(any_out& r) {
- std::swap(streamer_, r.streamer_);
- std::swap(o_,r.o_);
- return *this;
- }
- friend std::ostream& operator<<(std::ostream& o,any_out& a) {
- if (a.streamer_) {
- a.streamer_->print(o,a.o_);
- }
- return o;
- }
- };
让我们来测试一下我们的 any_out 类。
- int main() {
- std::vector<any_out> vec;
- any_out a(std::string("I do have operator<<"));
- vec.push_back(a);
- vec.push_back(112);
- vec.push_back(65.535);
- // 打印vector vec中的所有东西
- std::cout << vec[0] << "\n";
- std::cout << vec[1] << "\n";
- std::cout << vec[2] << "\n";
- a=std::string("This is great!");
- std::cout << a;
- }
- any_out nope(X());
- std::cout << nope;
- }
谓词
在我们结束关于 any 用法的这一节之前,让我们看一下如何围绕 any 来建立一些功能,来简化使用和增加表现力。any 可用于在容器类中保存不同的类型,它可以很容易地保存这些值,但很难去操作它们。
首先,我们创建两个谓词,is_int 和 is_string, 它们分别用于判断一个 any 是否包含一个 int 或一个 string 。在我们想在一个存有不同类型对象的容器中查找特定类型时,或者是想测试一个 any 的类型以决定后面的动作时,这很有用。实现方法是用 any 的成员函数 type 来测试。
- bool is_int(const boost::any& a) {
- return typeid(int)==a.type();
- }
- bool is_string(const boost::any& a) {
- return typeid(std::string)==a.type();
- }
- template <typename T> bool contains (const boost::any& a) {
- return typeid(T)==a.type();
- }
对非空值计数
对于某些应用,可能要对容器中所有元素进行迭代并测试每个 any 是否含有值。一个空的 any 可能意味着要被删除,也可能我们要为了更一步的处理而取出所有非空的 any 元素。在一个算法中这是很常用到的,我们创建一个函数对象,它的函数调用操作符接受一个 any 参数。该操作符只是测试 any 是否为空,如果不是则递增计数器。
- class any_counter {
- int count_;
- public:
- any_counter() : count_(0) {}
- int operator()(const boost::any& a) {
- return a.empty() ? count_ : ++count_;
- }
- int count() const { return count_; }
- };
- int i=std::for_each(C.begin(),C.end(),any_counter()).count();
- any_counter counter;
- std::for_each(C.begin(),C.end(),counter);
- int i=counter.count();
从容器中取出某种类型的元素
下面是另一个好东西:一个从容器中取出某种类型元素的提取器。在把异类容器中的一部分传递给一个同类容器时,这是一个有用的工具。手工来做这件事是乏味且容易出错的,但一个简单的函数对象可以为我们照看好一切。我们泛化这个函数对象,用取出元素的输出迭代器的类型,以及要从传递给该函数对象的 any 参数中取出的类型来参数化。
- template <typename OutIt,typename Type> class extractor {
- OutIt it_;
- public:
- extractor(OutIt it) : it_(it) {}
- void operator()(boost::any& a) {
- Type* t(boost::any_cast<Type>(&a));
- if (t) {
- *it_++ = *t;
- }
- }
- };
- template <typename Type, typename OutIt>
- extractor<OutIt,Type> make_extractor(OutIt it) {
- return extractor<OutIt,Type>(it);
- }
现在该用一个例程来测试一下我们新的 any 同伴了。
- int main() {
- std::cout << "Example of using predicates and the "
- "function object any_counter\n";
- std::vector<boost::any> vec;
- vec.push_back(boost::any());
- for(int i=0;i<10;++i) {
- vec.push_back(i);
- }
- vec.push_back(boost::any());
- // 计算含有值的any实例的数量
- int i=std::for_each(
- vec.begin(),
- vec.end(),
- any_counter()).count();
- std::cout
- << "There are " << i << " non-empty any's in vec\n\n";
- // 从vec中取出所有int
- std::list<int> lst;
- std::for_each(vec.begin(),vec.end(),
- make_extractor<int>(std::back_inserter(lst)));
- std::cout << "Found " << lst.size() << " ints in vec\n\n";
- vec.clear();
- vec.push_back(std::string("This is a string"));
- vec.push_back(42);
- vec.push_back(3.14);
- if (is_string(vec[0])) {
- std::cout << "Found me a string!\n";
- }
- if (is_int(vec[1])) {
- std::cout << "Found me an int!\n";
- }
- if (contains<double>(vec[2])) {
- std::cout <<
- "The generic tool is sweeter, found me a double!\n";
- }
- }
Example of using predicates and the function object any_counter
There are 10 non-empty any's in vec
Found 10 ints in vec
Found me a string!
Found me an int!
The generic tool is sweeter, found me a double!
象以上这些小而简单的工具已经被证实是非常有用的。当然,不仅对 any 是这样;它是标准库容器和算法的设计中的一个特点。这些例子示范了如何与 any 一起使用组合函数。提供过滤、计数、操作特定类型等等,是隐藏实现细节的有效方法,并简单化了对 any 的使用。
遵守标准库适配器的要求
如果你觉得谓词 contains 很有用,你可能要注意它并不是到处都能用。它不能和标准库的适配器一起使用。下面的例子稍稍超出了本章的范围,但由于 any 是那么地适用于容器类,所以留下 contains 谓词的这点缺陷是不应该的。问题在于标准库的适配器(bind1st, bind2nd, not1, 和 not2)利用了它们所适配的谓词的一些必要条件。参数类型和结果类型必须用typedef暴露出来,这意味着我们需要的是函数对象而不是函数。
先来定义一个新的函数对象,contains_t. 它可以派生自辅助类 std::unary_function (它是C++标准库的组成部分,以便创建正确的 typedef),自动地定义参数类型和结果类型,但为了让事情更清楚,我们自己来提供所需的 typedef 。参数类型由 const boost::any& 改为 boost::any, 以避免产生到引用的引用,那是非法的。实现和前面的一样,这里只给出函数调用操作符。
- template <typename T> struct contains_t {
- typedef boost::any argument_type;
- typedef bool result_type;
- bool operator()(boost::any a) const {
- return typeid(T)==a.type();
- }
- };
- template <typename T> contains_t<T> contains() {
- return contains_t<T>();
- }
- template <typename T> bool contains(const boost::any& a) {
- return contains_t<T>()(a);
- }
- int main() {
- std::cout << "Example of using the improved is_type\n";
- std::vector<boost::any> vec;
- vec.push_back(std::string("This is a string"));
- vec.push_back(42);
- vec.push_back(3.14);
- if (contains<double>(vec[2])) {
- std::cout << "The generic tool has become sweeter! \n";
- }
- vec.push_back(2.52f);
- vec.push_back(std::string("Another string"));
- std::vector<boost::any>::iterator
- it=std::find_if(vec.begin(),vec.end(),contains<float>());
- if (it!=vec.end()) {
- std::cout << "\nPrint the float twice!\n";
- std::cout << boost::any_cast<float>(*it) << "\n";
- std::cout << *boost::any_cast<float>(&*it) << "\n";
- }
- std::cout <<
- "There are " << vec.size() << " elements in vec\n";
- vec.erase(std::remove_if(vec.begin(),vec.end(),
- std::not1(contains<std::string>())),vec.end());
- std::cout << "Now, there are only " << vec.size()
- << " elements left in vec!\n";
- }
这个类型可以包含不同类型的值,而且与无类类型(如 void*)有很大不同。我们总是严重地依赖C++中的类型安全,只有在极少数情形下我们会愿意没有它来干活。
这是有很好的原因的:类型安全防止我们犯错,并改善了我们代码的性能。因此,我们应该避免无类类型。还有,发现自己需要异类存储的情形很少见,或者为了将使用者隔离于类型的细节,或者为了在更低的层次获得极度的灵活性。any 提供了这些功能,同时维护了类型安全,它是我们的工具箱的最好扩充!
在以下情形时使用 Any 库:
- 你需要在容器中存放不同类型的值
- 需要保存未知类型
- 类型被传递到无须知晓任何有关该类型信息的层次
Any 的设计同时也是一门很有价值的课程,关于如何封装一个类型而不影响到该类型的封套类。这种设计可以用于创建泛型函数对象、泛型迭代器等等。它是一个展示封装的威力以及与模板相关的多态性的例子。
在标准库中,有很好的工具来存放多个元素。当需要存储异类的元素时,我们想避免使用新的集合类型。any 提供了一种方法,在大多数情况下它可以与已有容器一起使用。在某种程度上,模板类 any 扩展了标准库容器的能力,把不同的类型封入一个同类型的包装中,就可以把它们放入前述容器中了。
把 Boost.Any 加到已有代码中是很简单的。它不需要修改设计,并且立即就增加了灵活性。接口非常小,这使得它成为一个很容易理解的工具。
Any 库由 Kevlin Henney 创建,与所有 Boost 库一样,它由 Boost 社区复审、改进和强化。
Any 总结
这个类型可以包含不同类型的值,而且与无类类型(如 void*)有很大不同。我们总是严重地依赖C++中的类型安全,只有在极少数情形下我们会愿意没有它来干活。
这是有很好的原因的:类型安全防止我们犯错,并改善了我们代码的性能。因此,我们应该避免无类类型。还有,发现自己需要异类存储的情形很少见,或者为了将使用者隔离于类型的细节,或者为了在更低的层次获得极度的灵活性。any 提供了这些功能,同时维护了类型安全,它是我们的工具箱的最好扩充!
在以下情形时使用 Any 库:
- 你需要在容器中存放不同类型的值
- 需要保存未知类型
- 类型被传递到无须知晓任何有关该类型信息的层次
Any 的设计同时也是一门很有价值的课程,关于如何封装一个类型而不影响到该类型的封套类。这种设计可以用于创建泛型函数对象、泛型迭代器等等。它是一个展示封装的威力以及与模板相关的多态性的例子。
在标准库中,有很好的工具来存放多个元素。当需要存储异类的元素时,我们想避免使用新的集合类型。any 提供了一种方法,在大多数情况下它可以与已有容器一起使用。在某种程度上,模板类 any 扩展了标准库容器的能力,把不同的类型封入一个同类型的包装中,就可以把它们放入前述容器中了。
把 Boost.Any 加到已有代码中是很简单的。它不需要修改设计,并且立即就增加了灵活性。接口非常小,这使得它成为一个很容易理解的工具。
Any 库由 Kevlin Henney 创建,与所有 Boost 库一样,它由 Boost 社区复审、改进和强化。