您的位置:首页 > 其它

boost any类库的使用和内部实现细节

2015-11-03 22:35 417 查看

boost any类库的使用

功能

可以将任何类型值保存到boost::any类中。也可以通过boost::any_cast模板方法来将boost::any类中设置的数据,安全地拿出来。

基本小例子

将任何数据保存到any类中

#include <assert.h>
#include <boost/any.hpp>

#include <string>
#include <vector>
#include <map>

using namespace std;
using namespace boost;

class Student
{
public:
Student() : age_(25) {}
Student(int age) : age_(age) {}

int GetAge() const
{
return age_;
}
private:
int age_;
};

int calc_max(int data1, int data2)
{
return data1 > data2 ? data1 : data2;
}

int main(int argc, char **argv)
{
int a[] = {1, 2, 3, 4, 5};
vector<int> iv(a, a + 5);
map<string, int> info;
int i = 100;

info["zs"] = 10;
info["ls"] = 5;

//======================支持将任何类型通过构造函数来创建any对象======================

// 将基本数据类型保存到any类中
any any1(10);
any any2("hello world");
any any3(2.5f);
any any4(3.14);
any any5(a);
any any6(&i);

// 将对象保存到any类中
any any7(string("hello world"));
any any8(iv);
any any9(info);

// 将用户自己定义的类型对象保存到any类中
Student stu;
any any10(stu);

// 可以使用某个现有的any对象来进行拷贝构造
any any11(any9);

// ================也可以讲任何类型复制给any对象========
any10 = Student();
any10 = any3;

any10 = 1;
any10 = &calc_max;
any10 = any3;
any10 = vector<int>();

//any any12(int()); //千万不能这样写
//any any12(&calc_max);
//any12 = any10;

return 0;
}


怎么样,很吃惊吧,从上面的代码中,我们可以看到,我们可以将基本数值类型(int、double、float、char、各种指针)、标准库中鲜有的模板类对象、用户自己定义的各种类型。

而且any也支持对于自身的拷贝构造和赋值操作。你会发现,通过这种支持。你可以讲保存int数值的any对象赋值给保存字符串的any对象,那么保存字符串的any对象就会变成保存int数值的any对象。你会发现使用any来接收数据的话,就像使用了某种动态语言。对于c++这种静态语言来讲,真的是实在是太太惊艳了。

而且也支持将任何类型的数值或者对象赋值给现有的any对象。也就是说,你不但可以通过构造器来构造保存任意值的any对象,你还可以将已经保存了某种类型值的any对象中值改变成其他类型。真的是太像动态语言了。

下面说一个有趣的问题,也是使用值来构造any的一个注意点

any stu(Student()); //这儿是我想使用一个默认的Student对象来赋值给stu

注意这样写是不行的,为什么呢?因为这样写等价于any stu(Student(*)())。编译器会把它当做一个函数声明。

对于上面的,我们可以通过这样写any stu(Student(25));

或者: Student tmp; any stu(tmp);

从any对象中获取值

/*
* app.cpp
*
*  Created on: 2015年11月3日
*      Author: TwoFlyLiu
*/
#include <assert.h>
#include <boost/any.hpp>
#include <boost/type_index.hpp>

#include <string>
#include <vector>
#include <map>
#include <iostream>

using namespace std;
using namespace boost;

template <typename T>
struct append_const_to_ptr
{
typedef const T * type;
};

template <typename T>
struct append_const_to_ptr<const T>
{
typedef const T *type;
};

// 这个宏的设计意图是为了解决,boost any库以数组构造any对象,所产生的类型衰减问题。
// 经过测试:mingw编译器对于int a[]还是const int a[],最后衰减成的类型都是int const*类型。
// 而对于vc编译器来讲,则比较正常,对于int a[],会衰减成int *,对于const int a[],则会衰减成int const *
// 这个宏的目的,就是设置真正的衰减后的类型,这样在any_cast中,就不会发生类型转换的错误
#if defined(__GNUC__)
#define DECAY_ARRAY_TYPE(value_type) append_const_to_ptr<value_type>::type
#elif defined(_MSC_VER)
#define DECAY_ARRAY_TYPE(value_type) value_type *
#endif

int main(int argc, char **argv)
{
int a[5] = {1, 2, 3, 4, 5};
vector<int> iv(a, a + 5);
map<string, int> info;
int i = 100;

info["zs"] = 10;
info["ls"] = 5;

any object_or_val(i);

// 通过boost::any_cast来从any中获取任何类型的值。
// 有两大种any_cast重载函数,
// 一种是:
// template <typename ValueType> ValueType &any_cast(any &),可能会抛出bad_any_cast异常,当转换类型和实际类型不一致时候,会抛出异常
// template <typename ValueType> ValueType &any_cast(any *)

assert(100 == *any_cast<int>(&object_or_val));
try
{
assert(100 == any_cast<const int>(object_or_val));
}
catch(boost::bad_any_cast &e)
{
cout << "error" << endl;
}

object_or_val = a; //这儿需要注意,对于数组来讲,当他传进去时,内部类型会衰减成指针类型
cout << typeindex::type_index(object_or_val.type()) << endl;

// DECAY_ARRAY_TYPE自己定义的,用来解决vc编译器和mingw编译器衰减规则不同问题。
// 具体问题,看上面这个宏的定义
assert(a[0] == **any_cast<DECAY_ARRAY_TYPE(int)>(&object_or_val));
try
{
assert(a == any_cast<DECAY_ARRAY_TYPE(int)>(object_or_val));
} catch(boost::bad_any_cast &)
{
cout << "error" << endl;
}

object_or_val = iv;
assert(iv == *any_cast<vector<int> >(&object_or_val));
try
{
any_cast<vector<int> >(object_or_val);
}
catch(boost::bad_any_cast &)
{
cout << "error" << endl;
}

object_or_val = info;
assert((info == *any_cast<map<string, int> >(&object_or_val)));
try
{
any_cast<map<string, int> >(object_or_val);

// 使用指针作为参数,转换不成功会返回空指针
assert(0 == any_cast<string>(&object_or_val));

// 使用引用作为参数,转换不成功则会抛出boost::bad_any_cast异常
// 这儿因为类型不一致,所以内部会抛出异常(有意的,为了说明档转换成的类型和内部类型不一致的时候,会抛出异常)
// any_cast<string>(object_or_val);
}
catch(boost::bad_any_cast &)
{
cout << "Error" << endl;
}

// 其实还有unsafe_any_cast,非常不推荐使用它,强烈抵制啊
object_or_val = i;
cout << *unsafe_bad_cast<int>(&object_or_val) << endl;

return 0;
}


从上面的代码可以看出。从any对象中取出实际数据的模板函数式any_cast。这个函数4个重载。

template <typename ValueType> ValueType *any_cast(any *) BOOST_NOEXCEPT


template <typename ValueType> ValueType *any_cast(const any *) BOOST_NOEXCEPT


template <typename ValueType> ValueType &any_cast(any &)


template <typename ValueType> ValueType &any_cast(const any &)


template <typename ValueType> ValueType *unsafe_any_cast(any *) BOOST_NOEXCEPT


template <typename ValueType> ValueType *unsafe_any_cast(const any *) BOOST_NOEXCEPT


这个6个重载函数前四个方法其实分为两大类,一种,是用指针作为参数和返回值类型。这种事类C的编程风格,使用这种风格的函数,是不会抛出异常的,但是当转换失败的时候会返回空指针。还有一种是以引用作为参数和最终值类型,使用这种事c++编程风格,使用这种函数当要获取的类型和实际类型不符的时候,会抛出bad_any_cast异常

最后两个是进行不安全转换的,严重不推荐使用它,因为他连对传入的数据是否为空都没有检查,类型也没有检查,直接返回真正的数据。这样非常容易出现运行时错误的。当一定要使用他的时候,一定要保证传入unsafe_any_cast的实际参数不能为空。并且ValueType类型和any对象的真实类型的内存模型要保证兼容,这样才不会有运行错误。

使用any和any_cast有一个小的注意点

当any对象保存数组的时候,其实在any内部保存的类型是数组的衰减类型。但是对于不同的编译器,any内部的实现对于数组衰减的具体类型也不相同。具体:

编译器原来数组类型衰减后类型
vc++int a[5]int *
vc++const int a[5]int const *
mingwint a[5]int const*
mingwconst int a[5]int const*
从结果你可以看出,对于vc编译器则比较正常,但是对于mingw不管数组原型是什么,他的类型总是
int const *
。我在上面的小样例中给出了一个
DECAY_ARRAY_TYPE
宏,他可以解决解决两个编译器之间的差异性,只是对于其他编译器,我没有进行测试,我不知道他们的衰变规则。

访问/修改any对象的内部状态

#include <boost/any.hpp>
#include <iostream>
#include <assert.h>

using namespace std;
using namespace boost;

int main(int argc, char **argv)
{
int data = 10;
any value;

assert(value.empty()); //empty()方法用来返回内部是否保存数据
value = data;
assert(!value.empty()); //很明显当内部保存数据的时候,empty方法就不返回true了

// type方法用来返回内部类型信息boost::typeindex::type_info类型,其实这个类型是boost::typeindex::type_index类,内部数据类型。具体还请看boost TypeIndex库,可以看前面写的博客,也可以看官方文档
assert(typeindex::type_id<int>() == value.type());

// 输出any对象内部具体的数据类型
cout << "value type : " << typeindex::type_index(value.type()) << endl;

// 清除内部保存的数据,这样它又变成空了
value.clear();
assert(value.empty());

return 0;
}


any对象主要有如下三个访问/修改内部状态的方法。

方法名称功能小程序片段
bool empty()用来检测内部是否有数据
any a; assert(a.empty());
void clear()用来清除内部保存的数据,然后他会变成空的any对象
any a(5); a.clear(); assert(a.empty());
boost::typeindex::type_info type()用来保存内部真正的类型信息
any a(5); assert(a.type() == boost::typeindex::type_id<int>())
;
对于type()方法,他返回的是boost TypeIndex库(关于这个库的使用,可以参考我前面写的章节(包括使用和源码实现),或者参考boost官方文档)中的类型信息,所以当进行比较any对象的内部类型是否是某种具体类型时,推荐使用上面的那种写法,而不应该使用typeid,因为当rtti被禁用的时候,或者强制不兼容rtti宏被定义的时候,这样写就会出错。

有个小技巧

当你不确定某种类型在any对象中的具体类型时,你通过any对象的type方法来获取类型信息,然后通过此类型信息来构建boost::typeindex::type_index对象,这个对象支持输出流操作,他会输出便于人阅读的名称。

比如:

any a(10);

boost::typeindex::type_index type(a.type());

cout << type << endl;

他会输出int,这是个非常实用的小技巧

any类的swap方法

因为这个方法类似于stl中的swap方法,所以只提供一个小样例:

#include <boost/any.hpp>
#include <iostream>
#include <string>
#include <assert.h>

using namespace boost;
using namespace std;

int main(int argc, char **argv)
{
string hello("hello world");
int data = 20;

any val1(hello);
any val2(data);

assert(typeindex::type_id<string>() == val1.type());
assert(typeindex::type_id<int>() == val2.type());

val1.swap(val2);
assert(typeindex::type_id<string>() == val2.type());
assert(typeindex::type_id<int>() == val1.type());

swap(val1, val2);  //也支持全局的swap方法
assert(typeindex::type_id<string>() == val1.type());
assert(typeindex::type_id<int>() == val2.type());

return 0;
}


boost any类库的实现

这个类库主要包含两部分,一部分是any类的实现,另一部分时any_cast模板方法的实现。

any类的实现

any类的实现,包含成员数据和成员方法。成员方法在上面介绍这个类的使用的时候已经非常清楚了。主要是构造器,赋值运算符,和三个访问/修改内部状态的方法。内部成员其实也简单,如果你看源码的话,你会发现他只有一个place_holder类型的指针。

place_holder类的实现

namespace boost{
class any
{
...
// 我认为这儿placeholder就是一个抽象类(这儿类似于java中的接口概念)。通过它,我们可以获取内部数据类型,和拷贝自己信息到一个新对象中
class placeholder
{
public:
virtual ~placeholder() {}
public:
//获取内部数据类型
virtual const boost::typeinde::typ
1023f
e_info & type() const BOOST_NOEXCEPT =0;
//原型方法,用于拷贝自身对象信息到新的placeholder对象中
virtual placeholder * clone() const =0;
};
...
};
}


placeholder类是一个抽象类,只能通过它来获取内部类型信息和提供拷贝自身的能力。

他有真正的实现是holder类,他才是真正的用来保存数据类的地方。

holder类的实现

namespace boost{
class any
{
...
template <typename ValueType>
class holder : public placeholder
{
public:
// 通过指定类型数据来构造holder对象
holder(const ValueType &value) : held(value){}

// 如果支持右值引用,则利用右值引用提高程序性能
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
holder(ValueType&& value)
: held(static_cast< ValueType&& >(value))
{
}
#endif

public: // 实现placeholder中的抽象方法
virtual const boost::typeinde::type_info & type() const BOOST_NOEXCEPT
{
return boost::typeindex::type_id<ValueType>().type_info();
}
//原型方法,用于拷贝自身对象信息到新的placeholder对象中
virtual placeholder * clone() const
{
return new holder(held);
}

public:
ValueType held; // 真正内部保存的值
private:
// 当holder构造好的时候,不支持在关边内部的值。其实这儿实现比较无关主要,如果你禁用它的话,那么你也要拷贝右值引用的情况
holder & operator=(const holder &);
//下面这个方法是我后添加的,主要是为了和上面成对
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
holder & operator=(holder &&);
#endif
};
...
};
}


holder类是一个模板类,他实现类抽象类placeholder中的抽象方法。因为他是个模板方法,所以他可以用来保存任何类型的值。

any类的几种构造器和析构器实现

namespace boost {

class any
{
public:
// 空的话,则不保存任何数据
any() BOOST_NOEXCEPT : content(0) {}

//  利用placeholder的clone方法,就可以实现内部数据的拷贝了
any(const any &other)
: content(other.content ? other.content->clone() : 0) {}

// 一个关键构造,模板构造器,因为他any类才可以使用任何类型值来构造
// 通过这个构造器也可以看出,
// 不管ValueType是否有cv修饰,最终保存到holder类内部的类型总是不含有cv的
// 如果ValueType是数据的话,他也会通过decay type_traits类衰减成指针的。只是这儿还是会有上面提到的,对于不同编译器的衰减类型不同问题,关键还是看上面any类的使用部分。
template <typename ValueType>
any(const ValueType & value)
: content(new holder<typename remove_cv<typename decay<const ValueType>::type>::type>(value)) {}

//如果支持右值引用的话,则提供移动构造来优化程序性能
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
// 移动构造器
any(any&& other) BOOST_NOEXCEPT
: content(other.content)
{
other.content = 0;
}

// 如果ValueType不是any&引用类型,并且也不是常量的话,才会将value之完美转发到holder的held成员中。
// 这儿有意思的是disable_if这个模板类,利用它可以对ValueType的具体类型就行限制
template<typename ValueType>
any(ValueType&& value
, typename boost::disable_if<boost::is_same<any&, ValueType> >::type* = 0 // disable if value has type `any&`
, typename boost::disable_if<boost::is_const<ValueType> >::type* = 0) // disable if value has type `const ValueType&&`
: content(new holder< typename decay<ValueType>::type >(static_cast<ValueType&&>(value)))
{
}
#endif

// 析构器,删除content,这样不会发生内存泄露问题
~any()
{
delete content;
}

...
private:
placeholder *content; //用来表示真实的内部数据
};

}


从上面的构造器我们可以看到,

1. any类支持空构造器,这个时候他的数据成员content为空,

2. 支持拷贝构造器,对于内部数据content也利用这个数据的clone方法进行拷贝。

3. 还提供了一个模板构造器,利用它我们可以利用任何类型对象构造any对象。

4. 而且如果编译器支持c++11的右值引用的话,我们还提供了移动构造器,和对任意右值引用类型进行完美转发。这样就能大大优化程序性能。

并且通过第3点的实现部分,我们可以看到,在holder类中的数据类型是不带有cvr修饰的(cv是通过remove_cv去除的,r在decay中也会将他去除)。如果模板参数是数组的话,那么他也会通过decay模板类将数组衰减成指针类型。这儿的衰减规则,可能还会出现不同编译器,出现规则不同的问题。如果要修复这个问题,可能还要看decay模板类的实现问题。当然也不一定要深究,只要使用我上面写的DECAY_ARRAY_TYPE宏,可以解决vc++和mingw他们在这儿出现衰减规则不同的问题。简而言之,如果模板类型是带有cvr修饰的话,最终内部数据类型是不带cvr特性的。如果模板参数是数组类型,则最终内部数据类型是数组衰变后的指针类型。

any类重载的赋值运算符

namespace boost {

class any
{
public:
// 交换内部的数据成员。这个方法在重载==运算符中被大量使用
any & swap(any & rhs) BOOST_NOEXCEPT
{
std::swap(content, rhs.content);
return *this;
}

// 不支持右值引用的情况
#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
// 这儿为了避免发生资源泄露,使用了swap方法
// 细节:
// 1. 利用rhs构建了一个临时的any对象
// 2. 然后将临时any对象和*this对象进行交换。这样临时any对象保存*this中的数据,而*this则保存原来临时any对象利用rhs构建的数据
// 3. 当临时any对象超出作用域的时候,析构函数会被调用,内部数据content会被删除,这样原来的内部数据就会被删除调用这样就不会发生资源泄露问题
template <typename ValueType>
any & operator==(const ValueType &rhs)
{
any(rhs).swap(*this);
return *this;
}

// 有意使用值,不适用引用
// 因为如果使用引用的话,还要在内部构建一个临时变量,然后进行交换,还不如让编译器帮我们构建临时变量
any & operator==(any rhs)
{
//rhs.swap(*this);
any(rhs).swap(*this); //这儿是原来boost库中的实现,不管我认为应该使用上面的写法,这样少构造一个any对象
return *this;
}

// 支持右值情况,则利用右值进行优化
#else
any & operator==(const any & rhs)
{
any(rhs).swap(*this);
return *this;
}

// 这儿因为右值引用他是不会发生超出作用域析构的,所以新建了一个临时any对象来析构右值引用中的值,这儿是个小技巧
any & operator==(any &&rhs)
{
rhs.swap(*this);
any().swap(rhs);
return *this;
}

// 直接利用任意右值引用数值来优化any对象的构造
// 这儿仍然是通过static_cast<ValueType&&>来实现完美转发
// 仍然利用swap方法和临时变量来保证原来的any对象内部的资源会被释放掉
template <typename ValueType>
any & operator==(const ValueType &value)
{
// 这儿static_cast<ValueType&&>(value)等价于forward<ValueType>(value)
any(static_cast<ValueType&&>(value)).swap(*this);
return *this;
}
#endif
...
};
}


正如上面所见,any类重载了对于any对象的赋值运算符和重载了对于任意类型的赋值运算符。因为这两个重载,所以any对象支持将任意any对象赋值给任意any对象,不管他们的内部数据是否一致。也支持将任意类型赋值给any对象,并且原来any对象的内部数据也会被完全释放掉。

any类访问/修改内部状态方法

namespace boost {

class any
{
public:
...
// 原来获取内部数据是否为空
bool empty() const BOOST_NOEXCEPT
{
return !content;
}

// 删除内部数据,仍然利用,临时变量和swap方法
// 这种手段可以保证资源能被安全释放掉
void clear() BOOST_NOEXCEPT
{
any().swap(*this);
}

// 用来获取内部保存的数据类型信息
// 如果内部没有数据的话,则保存的是void的数据类型,如果有数据的话
//则直接调用placeholder接口提供的type方法
const boost::typeindex::type_info & type() const BOOST_NOEXCEPT
{
return content ? content->type() : boost::typeindex::type<void>().type_info();
}
...
};

}


上面的三种方法,在源码中都非常简单,这儿就不进行重复说明了。

全局swap方法

很简单,看源码了:

namespace boost
{
inline void swap(any & lhs, any & rhs) BOOST_NOEXCEPT
{
lhs.swap(rhs);
}
}


any_cast模板方法的实现

如果编译器支持类的友元模板函数的话,那么下面的两个模板方法:

template<typename ValueType>ValueType * any_cast(any *) BOOST_NOEXCEPT
template<typename ValueType>ValueType * any_cast(any *) BOOST_NOEXCEPT
都是any类的友元类。

具体源码表现在any类定义中:

namespace boost {
class any
{
// 如果支持成员友元模板
#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
private: // representation

template<typename ValueType>
friend ValueType * any_cast(any *) BOOST_NOEXCEPT;

template<typename ValueType>
friend ValueType * unsafe_any_cast(any *) BOOST_NOEXCEPT;
#endif
};
}


小补充

其实placeholder类和holder类还有一点没有说,如果编译器支持成员友元模板的话,则他们的访问限制为private,否则为public。源码也很简单和上面类似,自己看找源码吧。

any_cast指针版本的实现源码如下:

namespace boost{

//因为是友元,或者holder和placeholder设置为public,所以他能访问到any中的内部数据和内部定义的两个lz

// 功能就是讲operand转换为ValueType值,以指针形式返回
// 细节:
// 1. 检测operand是否为空,如果为空,则直接返回0
// 2. 检测ValueType不带cv修饰的类型是否和any对象内部数据类型一致,
// 只有一致的时候,才会将真正的数据进行返回,否则返回0
template<typename ValueType>
ValueType * any_cast(any * operand) BOOST_NOEXCEPT
{
// typename remove_cv<ValueType>::type获取的是不带cv修饰的类型
return operand && operand->type() == boost::typeindex::type_id<ValueType>()
? &static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
: 0;
}

// 内部就是调用非const版本,所以功能和上面的方法完全一致,详情,看上面的那个方法
template<typename ValueType>
inline const ValueType * any_cast(const any * operand) BOOST_NOEXCEPT
{
return any_cast<ValueType>(const_cast<any *>(operand));
}
}


在这儿我要介绍一下bad_any_cast异常类,因为他在any_cast的对于引用的版本中被用到。源码如下:

namespace boost{

//一个常量类,当rtti关闭的时候,继承std::bad_cast异常类
//当rtti启用的时候,继承std::exception异常
class bad_any_cast:
#ifdef BOOST_NO_RTTI
public std::bad_cast
#else
public std::exception
#endif
{
public:
virtual const char * what() const BOOST_NOEXCEPT_OR_NOTHROW
{
return "boost::bad_any_cast: "
"failed conversion using boost::any_cast";
}
};


很简单吧,就是一个异常类。

any_cast的引用版本源码:

namespace boost {

// 他是any_cast引用版本的核心实现部分,其他版本都是调用它
// 他的内部实现依赖于any_cast的指针版本
// 细节:
// 1. 利用any_cast的指针版本来指针返回值
// 2. 如果获取到的返回值为0的话,则抛出bad_any_cast异常
// 3. 最后将返回值的引用类型返回
template<typename ValueType>
ValueType any_cast(any & operand)
{
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref; // 去除ValueType的引用修饰

// 调用的是any_cast的指针版本
nonref * result = any_cast<nonref>(&operand);

// 值为空的话,则抛出异常
if(!result)
boost::throw_exception(bad_any_cast());

// Attempt to avoid construction of a temporary object in cases when
// `ValueType` is not a reference. Example:
// `static_cast<std::string>(*result);`
// which is equal to `std::string(*result);`
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<
boost::is_reference<ValueType>,
ValueType,
BOOST_DEDUCED_TYPENAME boost::add_reference<ValueType>::type
>::type ref_type; // ValueType的引用值

// 用来避免构造多余的临时变量
return static_cast<ref_type>(*result);
}

// 调用上面的版本
// 他在内部首先把引用修饰给去除掉了,然后调用上面的版本
// 所以他的行为和上面版本的行为基本一致,详细看上面的那个方法
template<typename ValueType>
inline ValueType any_cast(const any & operand)
{
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;
return any_cast<const nonref &>(const_cast<any &>(operand));
}

// 获取右值any对象的数据,因为any对象是右值,其内部数据也是右值
// 而右值只有右值和const引用能够接受,所以内部首先有个static_assert
// 用来保证ValueType是右值引用,或者常量引用
// 内部调用的是any_cast的引用版本,所以他也可能会抛出异常,具体看ValueType & any_cast(any &operand)这个版本方法
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
template<typename ValueType>
inline ValueType any_cast(any&& operand)
{
BOOST_STATIC_ASSERT_MSG(
boost::is_rvalue_reference<ValueType&&>::value /*true if ValueType is rvalue or just a value*/
|| boost::is_const< typename boost::remove_reference<ValueType>::type >::value,
"boost::any_cast shall not be used for getting nonconst references to temporary objects"
);
return any_cast<ValueType>(operand);
}
#endif
}


对于any_cast方法进行简单小结一下:

首先,any_cast的指针版本和引用版本他们都能实现安全转换,当operand为0,或者转换类型和实际类型不一致的时候,指针版本返回0,引用版本抛出boost::bad_any_cast异常。
any_cast支持包括常量和非常常量的真正和引用版本。也就是说他们可以接收非const类型,可以接收const类型,支持比较全。


unsafe_any_cast的实现如下源码:

namespace boost {
// 不检查operand是否为空,类型是否一致,直接访问内部数据,可能会造成严重错误,用时要小心。不然让operand位空,或者类型不一致
template<typename ValueType>
inline ValueType * unsafe_any_cast(any * operand) BOOST_NOEXCEPT
{
return &static_cast<any::holder<ValueType> *>(operand->content)->held;
}

//调用的是上面版本,详情见上面
template<typename ValueType>
inline const ValueType * unsafe_any_cast(const any * operand) BOOST_NOEXCEPT
{
return unsafe_any_cast<ValueType>(const_cast<any *>(operand));
}
}


对unsafe_any_cast总结一下

正如他们名称一样,他们是执行的操作时不安全转换。他们不会检测operand是否为空,要转换成的类型是否和any对象一致。使用的时候有时会引起严重错误的。所以使用的时候你要保证:

operand不能为空

ValueType和any对象内部类型要一致,或者内存模型至少要兼容。

总结

我首先介绍了any类库的基本使用,然后调用了any类库的实现细节。any在boost库中的源码文件为:boost/any.hpp。

使用any类库注意点:

1. 不能使用某个类的默认构造函数来构造any对象,比如
any a(int())
,原因见上面使用部分描述。

2. 使用数组来构造any对象时,注意实际类型会帅变成指针类型。并且不同编译器可能衰变规则也不一样,规则见上面

3. 对于any对象的内部数据中的真实类型包不包含cvr修饰的

4. 获取值时候,可以使用any_cast方法,可以使用指针版本,则返回的是指针值,如果失败的会则会返回空指针。也可以使用引用版本,如果失败则会抛出boost::bad_any_cast异常

5. boost any类库还提供了unsafe_bad_cast版本,看他的实现,个人感觉也太恶心了吧。使用不当的话,会出现运行时错误,强烈抵制使用它。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  boost