boost::any浅析
boost::any简介
C语言中有void*指针用于存放、传递任意类型数据,C++中呢?
boost库就提供了这样一个类:boost::any,一个很短小的类,主要作用是定义一个变量存放任意类型的数据。
boost::any用法
可以在存储的时候,将要存储的对象类型转换为boost::any类型,而要使用的时候,用boost::any_cast转型为相应的类型。
注意:转型的时候,只能转型为存储时的类型或者能隐式转换的类型,如果转换为不能兼容的类型,可能会导致未定义行为。
#include <boost/any.hpp>
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main()
{
vector<boost::any> vec;
list<int> lst = {4,5,6};
vec.emplace_back(1);
vec.emplace_back(string("hello"));
vec.emplace_back(lst);
int d = boost::any_cast<int>(vec[0]); // 将vec[0]转型为int
cout << d << endl;
const string &s = boost::any_cast<string&>(vec[1]); // 将vec[1]转型为string&
cout << s << endl;
const list<int>& tmpLst = boost::any_cast<list<int>&>(vec[2]); // 将vec[2转型为list<int>&
for (auto const& e : tmpLst) {
cout << e << ' ';
}
cout << endl;
return 0;
}
当我们不确定存储的any是何种类型时,可以利用RTTI技术typeid操作符进行判断
#include <boost/any.hpp>
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main()
{
vector<boost::any> vec;
list<int> lst = {4,5,6};
vec.emplace_back(1);
vec.emplace_back(string("hello"));
vec.emplace_back(lst);
vector<boost::any>::iterator it; // vector<boost::any>迭代器
boost::any anyone;
for (it = vec.begin(); it != vec.end(); ++it) {
anyone = *it;
if (anyone.type() == typeid(int)) { // typeid获取指定类型type_info类信息
cout << boost::any_cast<int>(anyone) << endl;
}
else if(anyone.type() == typeid(string)) {
cout << boost::any_cast<string>(anyone) << endl;
}
else {
cout << "unknow type" << endl;
}
}
return 0;
}
boost:any源码
实现any功能主要由三个部分组成:
1)any类;
2)内部类holder保存数据,其基类placeholder声明接口;
3)获取数据时,通过any_cast转型;
any 类及内部类holder
any class中实际存储用户实参的是指针成员content,content实际指向的是holder类型对象,而holder是一个内部类,其基类placeholder。
placeholder主要定义2个接口,用于帮助any获取content所指向的对象类型信息,以及深度克隆any成员content所指对象。
holder实现placeholder接口,非常重要一点是内部持有held,通过传入any实参拷贝构造或移动构造而来。也就是说,如果传入any的是右值,实参对象会移动构造held;如果传入any的是左值,实参对象会拷贝构造held。
注意:
1)传给any的实参必须是对象(或基本类型)本身,可以是左值,也可以是右值,但不能是地址,除非要通过any保存的是指针本身。
class any{ public: // structors
// 一组构造函数
any() BOOST_NOEXCEPT
: content(0)
{
}
template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value)) // 构造一个holder对象, 并由content接管
{
}
any(const any & other)
: content(other.content ? other.content->clone() : 0) // 拷贝other所指对象, 深度拷贝
{
}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
// Move constructor
any(any&& other) BOOST_NOEXCEPT
: content(other.content)
{
other.content = 0;
}
// Perfect forwarding of ValueType
template<typename ValueType>
any(ValueType&& value, typename boost::disable_if<boost::is_same<any&, ValueType> >::type* = 0)
: content(new holder< typename remove_reference<ValueType>::type >(static_cast<ValueType&&>(value)))
{
}
#endif
// 析构函数
~any() BOOST_NOEXCEPT
{
delete content;
}
public: // modifiers
// swap成员函数
any & swap(any & rhs) BOOST_NOEXCEPT
{
std::swap(content, rhs.content);
return *this;
}
#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
any(rhs).swap(*this);
return *this;
}
any & operator=(any rhs)
{
any(rhs).swap(*this);
return *this;
}
#else
any & operator=(const any& rhs)
{
any(rhs).swap(*this);
return *this;
}
// move assignement
any & operator=(any&& rhs) BOOST_NOEXCEPT
{
rhs.swap(*this);
any().swap(rhs);
return *this;
}
// Perfect forwarding of ValueType
template <class ValueType>
any & operator=(ValueType&& rhs)
{
any(static_cast<ValueType&&>(rhs)).swap(*this);
return *this;
}
#endif
public: // queries
bool empty() const BOOST_NOEXCEPT
{
return !content;
}
const std::type_info & type() const
{
return content ? content->type() : typeid(void);
}
#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
private: // types
#else
public: // types (public so any_cast can be non-friend)
#endif
class placeholder
{
public: // structors
virtual ~placeholder()
{
}
public: // queries
// 获取类型信息
virtual const std::type_info & type() const = 0;
// 克隆对象
virtual placeholder * clone() const = 0;
};
template<typename ValueType>
class holder : public placeholder
{
public: // structors
holder(const ValueType & value)
: held(value) // 这里会发生拷贝构造, 通过value拷贝构造held
{
}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
holder(ValueType&& value)
: held(static_cast< ValueType&& >(value)) // 这里会发生移动构造, 通过value移动构造held
{
}
#endif
public: // queries
// 获取ValueType的类型信息, 返回type_info对象
virtual const std::type_info & type() const
{
return typeid(ValueType);
}
// 拷贝构造一个holder对象, 返回一个placeholder指针
virtual placeholder * clone() const
{
return new holder(held);
}
public: // representation
ValueType held; // held是对象类型, ValueType是要存储对象的原生类型, 通过模板参数传入
private: // intentionally left unimplemented
holder & operator=(const holder &); // 没有实现
};
#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;
#else
public: // representation (public so any_cast can be non-friend)
#endif
placeholder * content;
};
转型any_cast
any通过统一的placeholder类型指针content保存传入实参对象,那么当要使用时,如何知道取出并转换成所需类型呢?
boost为any提供转型操作any_cast<>,any_cast可以将any保存的对象转换成指定类型。
any_cast有多个重载版本,
//-----------------------------------------------------
// 一组any_cast函数模板
// 参数是any*
template<typename ValueType>
ValueType * any_cast(any * operand) BOOST_NOEXCEPT
{
// 当operand非空, 且所指对象类型名与 模板参数ValueType类型名相同时,
// 将operator所指对象用static_cast转型为模板参数对应的指针类型; 否则, 返回空指针
return operand &&
#ifdef BOOST_AUX_ANY_TYPE_ID_NAME
std::strcmp(operand->type().name(), typeid(ValueType).name()) == 0
#else
operand->type() == typeid(ValueType)
#endif
? &static_cast<any::holder<ValueType> *>(operand->content)->held
: 0;
}
// 参数是const any*
template<typename ValueType>
inline const ValueType * any_cast(const any * operand) BOOST_NOEXCEPT
{
return any_cast<ValueType>(const_cast<any *>(operand));
}
// 参数是any&, 注意引用不能是空, 否则抛出异常
// 由于返回值是ValueType, 会发生对operator代表对象的拷贝构造
template<typename ValueType>
ValueType any_cast(any & operand)
{
// 定义内嵌类型nonref, 可用于去除引用(左值引用, 右值引用)
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
// If 'nonref' is still reference type, it means the user has not
// specialized 'remove_reference'.
// Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro
// to generate specialization of remove_reference for your class
// See type traits library documentation for details
BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif
// result是operand去除引用的指针类型
nonref * result = any_cast<nonref>(&operand);
if(!result)
boost::throw_exception(bad_any_cast());
return *result;
}
// 参数是const any&
template<typename ValueType>
inline ValueType any_cast(const any & operand)
{
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
// The comment in the above version of 'any_cast' explains when this
// assert is fired and what to do.
BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif
// 将operator转型为const nonref& (这里ValueType是去除const, 引用后的ValueType)
return any_cast<const nonref &>(const_cast<any &>(operand));
}
不安全转型unsafe_any_cast
类似于安全转型any_cast,但不做any实际存储对象类的与要转型的模板是否一致的类型验证。不推荐使用。
// Note: The "unsafe" versions of any_cast are not part of the
// public interface and may be removed at any time. They are
// required where we know what type is stored in the any and can't
// use typeid() comparison, e.g., when our types may travel across
// different shared libraries.
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));
}
使用boost::any注意事项
虽说boost::any能存放任意类型,但调用了被存放对象的成员函数。如果被存放对象禁用copy操作,那么就不能直接存放对象本身,因为会调用被存放对象的copy构造函数。
且看下面代码:
#include "muduo/base/noncopyable.h"
#include <boost/any.hpp>
#include <iostream>
#include <memory>
using namespace std;
class Context : muduo::noncopyable // 禁用拷贝
{
public:
Context(int val)
: val_(val)
{}
void print()
{
cout << "val = " << val_;
}
private:
int val_;
};
int main()
{
auto context = new Context(10);
boost::any any = context;
auto conn = boost::any_cast<Context>(&any);
conn->print();
return 0;
}
Context 类禁用拷贝,如果直接将context通过boost::any any = context;
,会调用template<typename ValueType> any & operator=(const ValueType & rhs)
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
any(rhs).swap(*this); // 这里用rhs构造any,因此会调用any的构造函数
return *this;
}
上面代码中,用rhs构造了临时对象any,因此会调用any的构造函数
template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value)) // 构造一个holder对象, 并由content接管
{
}
注意到这里ValueType类型可以推导出来为Context类型,而new holder<ValueType>(value)
会导致调用ValueType即Context的拷贝构造函数,而Context的拷贝构造函数已经禁用,这样做会导致错误,甚至程序崩溃。
解决办法:
将any存放对象由禁用拷贝到Context对象,改为指向Context对象的智能指针shared_ptr,来替代直接存放对象。
int main()
{
shared_ptr<Context> context(new Context(10)); // 存放对象改为shared_ptr<>,允许拷贝
boost::any any = context;
auto conn = boost::any_cast<shared_ptr<Context>>(any);
conn->print();
return 0;
}
参考
https://blog.csdn.net/u010216743/article/details/77772078
https://zhuanlan.zhihu.com/p/326781049