仿boost::any的泛型指针类any的实现
在boost库中,any是一种特殊容器,只能容纳一个元素,但这个元素可以是任意的类型----int、double、string、标准容器或者任何自定义类型。程序可以用any保存任意的数据,也可以在任何需要的时候取出any中的数据。any类目前已经加入到c++17标准中,在vs2017中include<any>头文件即可使用。
vs2017里的标准库any的头文件最后有句提示:
#pragma message("class any is only available with C++17 or later.") 也就是说此any类无法在支持C++17标准之前的编译器中使用。
仿boost的any实现如下,代码加了注释,方便读者学习理解
//any类设计要点
//1.any类不能是一个模板类形如int i;any<int>anyValue=i;无意义,还不如直接写int anyValue =i;
//2.any必须提供模板构造函数(赋值操作不必是模板的),才能完成如下操作:
// int i; long j; struct X; X x;any anyValue(i); anyValue=j; anyValue =x;
//3.必须提供某些有关它所保存的对象型别的信息。
//4.它必须能保存数值且提供某种方法将它保存的数值“取出来”。
//5.数据不能放在any类里,这会使any类成为模板类,不符合1的要求。
// 数据应该动态存放,即动态分配一个数据的容器来存放数据,而any类中则保存指向这个容器的指针。
// 明确地说,是指向这个容器的基类的指针,这是因为容器本身必须为模板,而any类中的指针成员又必须不是泛型的。
// 因为any不能是泛型的,所以any中所有数据成员都不能是泛型的。
// 所以,结论是:为容器准备一个非泛型的基类,而让指针指向该基类。
#pragma once
#include <memory>
#include <typeindex>
#include <exception>
#include <iostream>
struct Any
{
Any(void) : m_tpIndex(std::type_index(typeid(void))) {
std::cout << "Any(void) function called!" << std::endl; }
Any(const Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {
std::cout << "Any(const Any& that) called!" << std::endl; }
Any(Any && that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {
std::cout << " Any(Any && that) function called!" << std::endl; }
//创建智能指针时,对于一般的类型,通过std::decay来移除引用和cv符,从而获取原始类型
//cv符即const和violate
//类似int i;long j;anyValue(i),anyValue(j)时调用模板构造函数
//注意构造函数中的new Derived。由于Derived是模板类,U里包含的类型信息可以完整的被保留。
//构造函数本身又可以传进任意类型,这样就实现了类型类型擦除
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>(std::forward<U>(value))),\
m_tpIndex(std::type_index(typeid(typename std::decay<U>::type))){
std::cout << "Any(U && value) template function called!" << std::endl;}
template<class U> bool Is() const
{
std::cout << "Any::Is() template function called!" << std::endl;
return m_tpIndex == std::type_index(typeid(U));
}
bool IsNull() const {
std::cout << "Any::IsNull() function called!" << std::endl;
return !bool(m_ptr);
}
//AnyCast方法的思想是期望程序员们清楚自己在做什么,要不然就给他个异常瞧瞧。 //AnyCast模仿了boost提供的any_cast<>模板方法,在这里是Any的成员函数
//取出原始类型,需要客户提供类型信息 如 anyValue.AnyCast<int>
//m_tpIndex里保存了关于原始数据的型别信息,不存在boost::any实现时丢失型别信息的情况
//所以boost单独提供了any_cast版本出来,而这里不需要
template<class U>
U& AnyCast()
{
std::cout << "Any::AnyCast() template function called!" << std::endl;
//判断类型信息是否匹配,不匹配报错
if (!Is<U>())
{
std::cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << std::endl;
throw std::logic_error{ "bad cast" };
}
//匹配了,则取出原始数据
auto derived = dynamic_cast<Derived<U>*> (m_ptr.get());
return derived->m_value;
}
//赋值操作重载不是模板函数,不存在模板无穷递归即循环赋值问题
Any& operator=(const Any& a)
{
std::cout << "Any& operator= function called!" << std::endl;
//防止自赋值
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;
//泛型数据容器Derived的非泛型基类
struct Base
{
virtual ~Base() {
std::cout << " virtual ~Base() function called!" << std::endl;
} //虚析构函数,为保证派生类对象能用基类指针析构
virtual BasePtr Clone() const = ; //复制容器
}; //Derived是个模板类,各个类成员之间可以共享类模板参数的信息
//所以,可以方便地用原数据类型来进行各种操作
template<typename T>
struct Derived : Base
{
template<typename U>
Derived(U && value) : m_value(std::forward<U>(value)) {
std::cout << "Derived(U && value) function called!" << std::endl;
}
BasePtr Clone() const
{
std::cout << "Derived::Clone() function called!" << std::endl;
return BasePtr(new Derived<T>(m_value)); //改写虚函数,返回自身的复制体,动态构造
}
T m_value; //原始数据保存的地方
};
//any类转发Clone操作给BasePtr的Clone方法,实现数据复制
BasePtr Clone() const
{
std::cout << "Any::Clone() function called!" << std::endl;
if (m_ptr != nullptr)
return m_ptr->Clone();
return nullptr;
}
//通过基类指针擦除具体类型
//any类赋值时需要创建派生类对象,这里用std::unique_ptr管理派生类对象生命周期
BasePtr m_ptr;
std::type_index m_tpIndex; //提供关于型别的信息
};
测试代码如下:
// testany.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "any.hpp"
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
Any n;
std::cout << n.IsNull() << std::endl;
std::string str = "Hello";
//赋值操作,其实分两步进行
//先调用any的移动构造函数把str转换为any类型
//再调用any的赋值构造函数完成真正的赋值
n = str;
try
{
std::cout << n.AnyCast<int>() << '\n';
}
catch (std::exception& e)
{
std::cout << "Exception: " << e.what() << '\n';
}
std::cout << std::endl;
std::cout << "-----------------------------" << std::endl;
//直接初始化只调用移动构造函数,比采用赋值构造少了很多步骤,所以直接初始化效率更高
Any n1(1);
std::cout << n1.Is<int>() << '\n';
std::cout << std::endl;
std::cout << "-----------------------------" << std::endl;
Any n2(std::move(n1));
std::cout << n2.AnyCast<int>() << '\n';
std::cout << std::endl;
system("pause");
return ;
}
在vs2013上测试结果如下:
参考:
C++中的类型擦除(type erasure in c++)
仿boost::any的泛型指针类any的实现的更多相关文章
- Boost源码剖析之:泛型指针类any
C++是强类型语言,所有强类型语言对型别的要求都是苛刻的,型别一有不合编译器就会抱怨说不能将某某型别转换为某某型别,当然如果在型别之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非exp ...
- (七)boost库之单例类
(七)boost库之单例类 一.boost.serialzation的单件实现 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一 ...
- Boost中的智能指针(转)
这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...
- sp<> 强指针类的用法
在android 中可以广泛看到的template<typename T>, class Sp 句柄类实际上是android 为实现垃圾回收机制的智能指针.智能指针是c++ 中的一个概念 ...
- RestTemplate将响应数据转换为具有泛型的类对象
前言: 重要,RestTemplate在SpringBoot项目里即便通过HttpMessageConverters添加了Fastjson且优先级比jackson要高也不会在RestTemplate里 ...
- C++之不带指针类的设计——Boolean
经典的类设计分类 带指针类 不带指针类 Header文件的布局 #ifndef __COMPLEX__ #define __COMPLEX__ #include <iostream.h> ...
- 智能指针类模板(上)——STL中的智能指针
智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...
- C++中的智能指针类模板
1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...
- Iterator泛型指针
Iterator泛型指针 每个标准容器都提供一个名为: begin()的操作函数,返回一个iterator指向第一个元素: end()操作函数,返回一个iterator指向最后一个元素的下一位置: 定 ...
随机推荐
- ios7自定义返回按钮后,右滑返回功能失效解决方法
-(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //开启ios右滑返回 if ([ ...
- Spring中Bean及@Bean的理解
Spring中Bean及@Bean的理解 Bean在Spring和SpringMVC中无所不在,将这个概念内化很重要,下面分享一下我的想法: 一.Bean是啥 1.Java面向对象,对象有方法和属性, ...
- Sphinx 与全文索引
全文索引创建过程 第一步:将源文档传给分词组件(Tokenizer) 分词组件做了以下事情: 将文档分成一个一个的单词 去除标点符号 去除停词:英文(the / a / this / that ... ...
- JavaScript: DOM Docunment
Meaning: In browser , we exchange data using JavaScript code with user. We should know that most of ...
- SQL Server中有关约束(constraint)的一些细节
本文出处:http://www.cnblogs.com/wy123/p/7350265.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...
- spring boot+kafka整合
springboot版本是2.0.4 首先,在maven中引入spring-kafka的jar包 <dependency> <groupId>org.springframewo ...
- 吴裕雄 python深度学习与实践(12)
import tensorflow as tf q = tf.FIFOQueue(,"float32") counter = tf.Variable(0.0) add_op = t ...
- PC滚动条样式
#jmwin2为外部容器 #jmwin2{ width: 90%; height: 65%; background: white; position: abso ...
- vue 根据接口返回的状态码判断用户登录状态并跳转登录页,登录后回到上一个页面(http拦截器)
背景:后台接口返回code==501表示用户是未登录状态,需要登录才可访问: 通过http拦截做路由跳转 第一步:src目录下新建http.js文件,内容如下: import Axios from ' ...
- 微信H5授权登录,公众平台,开放平台
首先,特别不喜欢做微信开发,各种设置,各种文档,各种坑. 最近做一个H5网页,微信扫码打开,需要使用微信登录,获取用户的基本信息,自动保存,自动登录. 1.先去微信公众平台https://mp.wei ...