上一篇博文用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的更多相关文章

  1. (原创)用c++11打造好用的variant

    variant类似于union,它能代表定义的多种类型,允许将不同类型的值赋给它.它的具体类型是在初始化赋值时确定.boost中的variant的基本用法: typedef variant<in ...

  2. (原创)用c++11打造好用的variant(更新)

    关于variant的实现参考我前面的博文,不过这第一个版本还不够完善,主要有这几个问题: 内部的缓冲区是原始的char[],没有考虑内存对齐: 没有visit功能. 没有考虑赋值构造函数的问题,存在隐 ...

  3. (原创)用c++11打造类似于python的range

    python中的range函数表示一个连续的有序序列,range使用起来很方便,因为在定义时就隐含了初始化过程,因为只需要给begin()和end()或者仅仅一个end(),就能表示一个连续的序列.还 ...

  4. 【原创】C++11:左值和右值(深度分析)

    ——原创,引用请附带博客地址 2019-12-06 23:42:18 这篇文章分析的还是不行,先暂时放在这以后再更新. 本篇比较长,需要耐心阅读 以一个实际问题开始分析 class Sub{} Sub ...

  5. 原创:goldengate从11.2升级到12.1.2

    goldengate从11.2升级到12.1.2 1.停止抽取进程 GGSCI (001.oracle.drs.dc.com) 286> stop EXTSJ01 2. 停止投递和复制进程 等待 ...

  6. 用c++11打造类似于python的range

    python中的range函数表示一个连续的有序序列,range使用起来很方便,因为在定义时就隐含了初始化过程,因为只需要给begin()和end()或者仅仅一个end(),就能表示一个连续的序列.还 ...

  7. [原创]EF架构随心所欲打造属于你自己的DbModel

    前言 我们都知道EF可以生成Dbmodel,系统生成的Model有时候并不是我们想要的,如何我们要生成自己的Model,那么久需要我们手动的去修改T4模版,T4是对“Text Template Tra ...

  8. (原创)c++11中 function/lamda的链式调用

    关于链式调用,比较典型的例子是c#中的linq,不过c#中的linq还只是一些特定函数的链式调用.c++中的链式调用更少见因为实现起来比较复杂.c++11支持了lamda和function,在一些延迟 ...

  9. (原创)C++11改进我们的程序之move和完美转发

    本次要讲的是右值引用相关的几个函数:std::move, std::forward和成员的emplace_back,通过这些函数我们可以避免不必要的拷贝,提高程序性能.move是将对象的状态或者所有权 ...

随机推荐

  1. BZOJ 4521 [CQOI2016]手机号码 - 数位DP

    Description 在$[L, R]$找出有几个数满足两个条件 : 1 : 不同时含有$4$ 和 $8$ 2 : 至少有$3$个相邻的数相同 Solution 非常容易的数位DP, $pos$ 为 ...

  2. Two Sum II - Input array is sorted LT167

    Given an array of integers that is already sorted in ascending order, find two numbers such that the ...

  3. oracle迁移

    #导出scott的数据,排除 table_a table_b expdp system/password schemas=scott directory=datadir dumpfile=scott_ ...

  4. ubuntu下使用fstab挂载硬盘时,属于root,如何把它改为属于一个用户的(如sgjm)

    http://zhidao.baidu.com/link?url=xnakfVD16EtunTSt3wBm153DyqHnXN3FSPO1E_2SpVmM5bmEIwICLA0N6zN85_ioQ3f ...

  5. 使用 jfreechart 生成 曲线、柱状图、饼状图、分布图 展示到JSP

    虽然现在JS做报表和图形展示已经非常普遍和漂亮了,但是不能忽略有jfreechart 这样一种东西! 这些翻阅资料,在看以前写的示例时发现了关于jfreechart 的简单示例,不管怎样发上来分享一下 ...

  6. 2018.12.31 bzoj4001: [TJOI2015]概率论(生成函数)

    传送门 生成函数好题. 题意简述:求nnn个点的树的叶子数期望值. 思路: 考虑fnf_nfn​表示nnn个节点的树的数量. 所以有递推式f0=1,fn=∑i=0n−1fifn−1−i(n>0) ...

  7. SPRING 集成 KAFKA 发送消息

    准备工作 1.安装kafka+zookeeper环境 2.利用命令创建好topic,创建一个topic my-topic 集成步骤 1.配置生产者 <?xml version="1.0 ...

  8. java socket之传输实体类对象

    一.TCP编程     TCP协议是面向连接的.可靠地.有序的,以字节流的方式发送数据.java实现TCP通信依靠2个类:客户端的Socket类和服务器端的ServerSocket类. 基于TCP通信 ...

  9. 如何在jsp和html页面上获取当前时间

    要想在JSP页面中获得当前时间并显示出来,首先得导入相关的Java包,然后创建Date对象. <%@page import="java.text.SimpleDateFormat,ja ...

  10. 20155205 2016-2017-2 《Java程序设计》第9周学习总结

    20155205 2016-2017-2 <Java程序设计>第9周学习总结 教材学习内容总结 第十六章 JDBC简介 厂商在实现JDBC驱动程序时,依方式可将驱动程序分为四种类型: JD ...