(原创)用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是将对象的状态或者所有权 ...
随机推荐
- Vim 基本配置
1.关闭vi的一致性模式 set nocompatible 2.配置backspace的工作方式 set backspace=indent,eol,start 3.显示行号 set number 4. ...
- vs2010打开qt的.pro文件时错误解决办法
注意:qt creator工程中一般都已经存在*.pro文件,里面存放着一些自己配置的包含头文件和lib库文的信息,最好不要再重新使用qmake -project生成,若重新生成,则可能要重新增加配置 ...
- 校园网ipv6连接问题
没有ipv6的信号:只需要进入网络适配器里面先禁用再启用即可.
- mybatis 一次执行多条语句
现在的一些互联网应用 为了提高性能,现在一般比较少的使用外键.不是不用,只是在创建数据库不标明外键关系,用程序去维护. 为了维护数据一致性,我们需要手动完成相关数据的删除 比如用户和用户的关注 当用户 ...
- sqlserver 误删数据库恢复
本文为转载 原文:https://blog.csdn.net/xwnxwn/article/details/53537841 由于长时间从事企业应用系统开发,前往用户现场升级.调试系统是比较常做的事情 ...
- 《团队-爬取豆瓣电影TOP250-设计文档》
搭建环境: 1.安装python3.4 2.安装pycharm集成开发环境 3.安装Git for Windows 4.安装python第三方包 bs4开发阶段: 1.团队成员申请并配置github账 ...
- mysql数据库的安装和基本使用
一.数据库安装配置 1)数据库的概念 .数据库相关概念 数据库服务器(本质就是一个台计算机,该计算机之上安装有数据库管理软件的服务端) 数据库管理管理系统RDBMS(本质就是一个C/S架构的套接字软件 ...
- centos7 sqoop 1 搭建笔记
1.require : java环境,hadoop,hive ,mysql2.下载解压sqoop13.设置环境变量 export SQOOP_HOME=/data/spark/bin/sqoop ex ...
- oracle count 大表
刚从生产环境导了一个大表到测试环境,迫不及待的要好好玩弄一下. 1.coun(1) ) from table_name; 条数: 567979280 时间:4:47 2.count 索引字段 sele ...
- C++IO cin
cin cin.get() 每次只读缓冲区一个字符,不能接收空格 cin.getline() 读缓冲区一行,能够接收空格 cin.ignore(2) 忽略缓冲器2个字节 int i = cin.pee ...