大家在编写程序的时候应该遇到过这样一个场景,该场景需要传递某种数据,但是数据类型和数据大小并不确定,这种时候我们常用void *
类型的变量来保存对象指针。例如:
struct SomeData { |
上面的结构体只是一个示例,代表有的数据是用户产生的。当用户数据是一个字符串时,可能的代码是:
SomeData sd{}; |
另外,使用void*
存储数据需要了解数据类型,并且需要自己维护数据的生命周期:
std::string *str = static_cast<std::string *>(sd.user_data); |
使用std::shared_ptr<void>
可以解决生命周期的问题:
struct SomeData { |
虽然std::shared_ptr<void>
可以用于管理生命周期,但是类型安全的问题却无法解决。比如当user_data
销毁时,由于缺乏类型信息会导致对象无法正确析构。
为了解决以上这些问题,C++17标准库引入std::any
。顾名思义就是可以存储任意类型,我们可以将其理解为带有类型信息的void*
。例如:
struct any { |
当然,std::any
的实现比这个要复杂的多,我们后面再讨论类型是如何被记录下来的。先来看看std::any
的用法:
|
除了转换为对象本身,还可以转换为引用:
auto& str1 = std::any_cast<std::string&>(a); |
当转换类型不正确时,std::any_cast
会抛出异常std::bad_any_cast
:
try { |
如果转换的不是对象而是对象指针,那么std::any_cast
不会抛出异常,而是返回空指针
auto* ptr = std::any_cast<std::string>(&a); |
请注意,使用std::any_cast
转换类型必须和std::any
对象的存储类型完全一致,否则同样会抛出异常,即使两者是继承关系。原因很简单,std::any_cast
是直接使用type_info
作比较:
const type_info* const _Info = _TypeInfo(); |
最后简单描述一下std::any
保证类型安全的原理:
首先是类型转换,刚刚已经提到了,std::any
会记录对象的type_info
,std::any_cast
使用type_info
作比较,只有完全一致才能进行转换。
其次为了保证类型正确的拷贝,移动以及生命周期结束时能够正确析构,在创建std::any
对象时生成一些函数模板实例,这些函数模板调用了类型的拷贝,移动以及析构函数。std::any
只需要记录这些函数模板实例的指针即可。拿析构简单举例:
template <class T> |