(原创)用c++11打造好用的any
上一篇博文用c++11实现了variant,有童鞋说何不把any也实现一把,我正有此意,它的兄弟variant已经实现了,any也顺便打包实现了吧。其实boost.any已经挺好了,就是转换异常时,看不到详情,和boost.variant一样的问题。实现any比实现variant要简单,需要解决的关键技术是类型擦除,关于类型擦除我之前的博文有介绍,想了解的童鞋点这里。
实现any的关键技术
any能容纳所有类型的数据,因此当赋值给any时,需要将值的类型擦除才行,即以一种通用的方式保存所有类型的数据。这里可以通过继承去擦除类型,基类是不含模板参数的,派生类中才有模板参数,这个模板参数类型正是赋值的类型,在赋值时,将创建的派生类对象赋值给基类指针,基类的派生类中携带了数据类型,基类只是原始数据的一个占位符,通过多态,它擦除了原始数据类型,因此,任何数据类型都可以赋值给他,从而实现了能存放所有类型数据的目标。当取数据时需要向下转换成派生类型来获取原始数据,当转换失败时打印详情,并抛出异常。由于any赋值时需要创建一个派生类对象,所以还需要管理该对象的生命周期,这里用unique_ptr智能指针去管理对象的生命周期。
下面来看看一个完整的any是如何实现的吧。
#include <iostream>
#include <string>
#include <memory>
#include <typeindex>
struct Any
{
Any(void) : m_tpIndex(std::type_index(typeid(void))){}
Any(const Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {}
Any(Any && that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {} //创建智能指针时,对于一般的类型,通过std::decay来移除引用和cv符,从而获取原始类型
template<typename U, class = typename std::enable_if<!std::is_same<typename std::decay<U>::type, Any>::value, U>::type> Any(U && value) : m_ptr(new Derived < typename std::decay<U>::type>(forward<U>(value))),
m_tpIndex(type_index(typeid(typename std::decay<U>::type))){} bool IsNull() const { return !bool(m_ptr); } template<class U> bool Is() const
{
return m_tpIndex == type_index(typeid(U));
} //将Any转换为实际的类型
template<class U>
U& AnyCast()
{
if (!Is<U>())
{
cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << endl;
throw bad_cast();
} auto derived = dynamic_cast<Derived<U>*> (m_ptr.get());
return derived->m_value;
} Any& operator=(const Any& a)
{
if (m_ptr == a.m_ptr)
return *this; m_ptr = a.Clone();
m_tpIndex = a.m_tpIndex;
return *this;
} private:
struct Base;
typedef std::unique_ptr<Base> BasePtr; struct Base
{
virtual ~Base() {}
virtual BasePtr Clone() const = ;
}; template<typename T>
struct Derived : Base
{
template<typename U>
Derived(U && value) : m_value(forward<U>(value)) { } BasePtr Clone() const
{
return BasePtr(new Derived<T>(m_value));
} T m_value;
}; BasePtr Clone() const
{
if (m_ptr != nullptr)
return m_ptr->Clone(); return nullptr;
} BasePtr m_ptr;
std::type_index m_tpIndex;
};
测试代码:
void TestAny()
{
Any n;
auto r = n.IsNull();//true
string s1 = "hello";
n = s1;
n = "world";
n.AnyCast<int>(); //can not cast int to string
Any n1 = ;
n1.Is<int>(); //true
}
再总结一下any的设计思路:Any内部维护了一个基类指针,通过基类指针擦除具体类型,any_cast时再通过向下转型获取实际数据。当转型失败时打印详情。
c++11 boost技术交流群:296561497,欢迎大家来交流技术。
(原创)用c++11打造好用的any的更多相关文章
- (原创)用c++11打造好用的variant
variant类似于union,它能代表定义的多种类型,允许将不同类型的值赋给它.它的具体类型是在初始化赋值时确定.boost中的variant的基本用法: typedef variant<in ...
- (原创)用c++11打造好用的variant(更新)
关于variant的实现参考我前面的博文,不过这第一个版本还不够完善,主要有这几个问题: 内部的缓冲区是原始的char[],没有考虑内存对齐: 没有visit功能. 没有考虑赋值构造函数的问题,存在隐 ...
- (原创)用c++11打造类似于python的range
python中的range函数表示一个连续的有序序列,range使用起来很方便,因为在定义时就隐含了初始化过程,因为只需要给begin()和end()或者仅仅一个end(),就能表示一个连续的序列.还 ...
- 【原创】C++11:左值和右值(深度分析)
——原创,引用请附带博客地址 2019-12-06 23:42:18 这篇文章分析的还是不行,先暂时放在这以后再更新. 本篇比较长,需要耐心阅读 以一个实际问题开始分析 class Sub{} Sub ...
- 原创:goldengate从11.2升级到12.1.2
goldengate从11.2升级到12.1.2 1.停止抽取进程 GGSCI (001.oracle.drs.dc.com) 286> stop EXTSJ01 2. 停止投递和复制进程 等待 ...
- 用c++11打造类似于python的range
python中的range函数表示一个连续的有序序列,range使用起来很方便,因为在定义时就隐含了初始化过程,因为只需要给begin()和end()或者仅仅一个end(),就能表示一个连续的序列.还 ...
- [原创]EF架构随心所欲打造属于你自己的DbModel
前言 我们都知道EF可以生成Dbmodel,系统生成的Model有时候并不是我们想要的,如何我们要生成自己的Model,那么久需要我们手动的去修改T4模版,T4是对“Text Template Tra ...
- (原创)c++11中 function/lamda的链式调用
关于链式调用,比较典型的例子是c#中的linq,不过c#中的linq还只是一些特定函数的链式调用.c++中的链式调用更少见因为实现起来比较复杂.c++11支持了lamda和function,在一些延迟 ...
- (原创)C++11改进我们的程序之move和完美转发
本次要讲的是右值引用相关的几个函数:std::move, std::forward和成员的emplace_back,通过这些函数我们可以避免不必要的拷贝,提高程序性能.move是将对象的状态或者所有权 ...
随机推荐
- 一种基于URL数据源的WEB报表插件
完全支持所见所得的报表设计, 支持 PHP ,Java 等所有支持JSON格式的后端. 立即下载测试版本 需要正式版本?请QQ联系:1565498246 或者留言
- Spring MVC 请求处理方法
以下两种都可以处理用户请求,但请求处理方法值得是第二种 1. SpringMVC 提供的 Controller 接口中公开的 ModelAndView handleRequest(request, r ...
- HTML5中的Web Notification桌面通知(右下角提示)
html5桌面通知(Web Notifications)对于需要实现在新消息入线时,有桌面通知效果的情况下非常有用,在此简单介绍一下这个html5的新属性.通过Web Notifications(桌面 ...
- Python module : simuPOP
conda config --add channels conda-forge conda install simupop simuPOP is a general-purpose individua ...
- OpenSSH配置与基本使用
SSH访问远程 SSH常见分类: telnet-远程登录协议,23/TCP 明文认证.明文传输(不安全) ssh(Secure SHell)-应用层协议,22/TCP 通讯和认证过程加密.主机认证 用 ...
- Spark的Rpct模块的学习
Spark的Rpct模块的学习 Spark的Rpc模块是1.x重构出来可,以前的代码中大量使用了akka的类,为了把akka从项目的依赖中移除,所有添加了该模块.先看下该模块的几个主要的类 使用E ...
- gcc -ldl 选项作用
如果你的程序中使用dlopen.dlsym.dlclose.dlerror 显示加载动态库,需要设置链接选项 -ldl 加载动态链接库,首先为共享库分配物理内存,然后在进程对应的页表项中建立虚拟页和物 ...
- 进入快速通道的委托(深入理解c#)
1.方法组:所有的名称相同的重载方法合在一起就成为一个方法组. 2.协变性和逆变性: 协变性指的是——泛型类型参数可以从一个派生类隐式转化为基类. 逆变性指的是——泛型类型参数可以从一个基类隐式转化为 ...
- Java 学习之集合类(Collections)
Collection(集合类) 我们是使用数组来保存数据,但是他的长度一旦创建,就已经确定了,当我们要动态传入穿值,数组就有些局限了,集合类就孕育而生:所谓集合,就是来保存,盛装数据,也可称为容器类: ...
- shell知识积累
Ubuntu下常用的快捷键:https://blog.csdn.net/u010771356/article/details/53543041 变量名和等号之间不能有空格,变量名中间不能有空格,可以使 ...