C++ any容器的介绍与简易实现

一、any容器是什么?

1、any“不是”模板类,any是一种很特殊的容器。
2、any只能容纳一个元素,但这个元素可以是任意的类型,可以是基本数据类型(int、double、string、标准容器或者任何自定义类型)。
3、一种动态(类型检查只发生在运行时)语言特性的数据结构。
4、C++17引入,需要RIIT支持,VS默认是没有支持C++17的,需要自己修改设置,如果不能使用any,请修改标准。

二、any类摘要

class any
{
public:
	any(); //默认构造
	any(const any& _That); //拷贝构造
	template<class _ValueType>
		any(_ValueType&& _Value);
	~any();	//析构
	any& operator=(const any& _That); //拷贝复制
	void reset();//清空内容
	void swap(any& _That); //交换语义
	bool has_value() const; //是否持有值
	const type_info& type() const; //获取类型信息
	template<class _Decayed>
	_Decayed * _Cast(); //若*this包含_decay类型的值,返回一个指向它的指针
};

//辅助函数
void swap(any& _Left, any& _Right); //交换语义
template<class _ValueType,class... _Types>
inline any make_any(_Types&&... _Args);	//工厂函数
template<class _Ty>
_Ty any_cast(const any& _Any);	//访问元素,运行时类型检查,不一致则抛出bad_any_cast异常。并且含有多个重载版本。

C++typeid关键字详解:

三、any类用法

#include<iostream>
#include<any>
#include<vector>
#include<set>
using namespace std;
int main() {
	any Int = 69,//整型
	Double = 8.8,	//浮点型
	CStr = "hello",	//字符数组
	Str = string("world!");	//string类
	vector<any> anys = { Int, Double, CStr, Str }; //可直接放入序列容器中
	//set < any > any_set{ Int,Double,CStr,Str }; //Error:不能直接放入关联容器内,需要提供operator<,但 any 很难比较
	cout << *Int._Cast<int>() << endl; //T* _Cast<typenampe T>():返回any对象的"值",但需要一个模板参数确定对象值的类型
	cout << *Double._Cast<double>() << endl;
	cout << *CStr._Cast<char const *>() << endl;
	cout << *Str._Cast<string>() << endl;
	
	std::any any = std::make_any<int>(10);
	if (typeid(int) == any.type())
		int i = std::any_cast<int>(any);
	//double d = std::any_cast<double >(any); //抛出bad_any_cast异常
	return 0;
}
//OutPut:
//69
//8.8
//hello
//world!

注意:
any的析构函数删除内部holder对象。如果类型是指针,any并不会对指针执行delete操作,所有any保存原始指针对造成内存泄漏。完美解决方法是用共享智能指针std::shared_ptr<T>包装存入any容器。

四、any实现原理:

通过使用模板构造函数擦除模板类的参数类型。

1、存储:
定义一个基类Base,再派生一个模板类Data,对二者再进行一次封装,构造一个Any类,使用Any类的模板构造函数来构造一个Data对象,这样就能存储任何数据类型。

2、取值:
只能存,但还无法把元素取出,所以Any必须有一个基类Base指针的成员变量,存储构造好的Data对象,使用模板函数_Cast(),利用其模板参数Type,进行一个再将Base类强制转换为Data< Type > 对象。

基本上是这个原理吧。

附超简版代码:

#include<iostream>
#include<any>
using namespace std;
class Any {
public:
	template<typename T>
	Any(T t) :base(new Data<T>(t)) {}//模板构造函数
	template<typename T>
	T _Cast() {
		return dynamic_cast<Data<T>*>(base.get())->value;	//强制转换
	}
private:
	class Base {
	public:
		virtual ~Base() {} //确定Base为多态类型
	};
	template <typename T>
	class Data :public Base {
	public:
		Data(T t) :value(t) {}
		T value;
	};
	unique_ptr<Base> base;	//基类指针
};
int main() {
	Any a(string("s123")), b = 1, c = 12.0;
	cout << a._Cast<string>() << endl;			/*调用get函数必须要填写模板参数,我感觉这样大大折扣了any的作用*/
	cout << b._Cast<int>() << endl;				/*我想能否在构造的时候就确认参数的类型,保证_Cast调用的时候不需要使用模板参数*/
	cout << c._Cast<double>();
	return 0;
}

参考

C++中VS2019下STL的std::any深入剖析
C++17 any类(万能容器)详解
Boost中的容器boost::any

文章来自:https://blog.csdn.net/qq_43148810/article/details/119389563

你可能还喜欢下面这些文章

为什么C++模板不支持分离式编译

前言最近编译C++代码时出现链接失败信息,类似下图:图一初见这个错误有些令人费解,不过经过一番分析,发现原因还是清晰的,和大家一起分享一下。图一中使用的tpl.h代码如下:tpl.cpp代码如下:main.cpp代码如下:我一直习惯把模板实现写在头文件中,因此从未遇见这种错误。这次偶然将模板声明和实现分离,出现了链接错误。这引发了我一些思考,为什么模板不支持分离式编译?模板的编译为了搞清楚模板是怎么编译的,这里以上述tpl.cpp中的compare模板函数为例。tpl.h中声明了一个模板函数并且在tpl.cpp中实现这个模板函数。现在我们编译tpl.cpp,生成的汇编代码如下:图二可以看出,一

C++入门:三、函数

这是我学习C++的第三篇笔记,函数。我的学习路径是现在学习的是函数的声明、定义、调用等相关知识。函数声明和定义函数的声明包含返回类型,函数名字,0个或者多个形参,无函数体,通常在头文件中对函数进行声明。函数的定义包含返回类型,函数名字,0个或多个形参,以及函数体。比如写一个求阶乘的函数,可以写成下面这样写一些简单的函数大多数语言都差不多,不过可惜每种语言或多或少都有自己的特色,这是比较令人头秃的地方。函数的参数函数可以带有0或多个参数,每个参数都需要声明类型。参数传递可以传值和传引用。如果形参是引用类型,那么它将绑定到对应的实参中,我们成为传引用。否则,将会把实参的值拷贝后赋值给形参,我们成为

Go语言的 make 和 new

new 和 make 是两个内置函数,主要用来创建并分配类型的内存。在我们定义变量的时候,可能会觉得有点迷惑,不知道应该使用哪个函数来声明变量,其实他们的规则很简单,new 只分配内存,make 只能用于 slice、map 和 channel 的初始化。下面我们就来具体介绍一下new在Go语言中,new 函数描述如下:从上面的代码可以看出,new 函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。同时 new 函数会把分配的内存置为零,也就是类型的零值。【示例】使用 new 函数为变量分配内存空间。当然,new 函数不仅仅能够为系统默认的数据类型,分配空间,自定义

Go入门:三、函数的声明和调用

这是我Go学习笔记的第三篇!接下来学习的是Go的函数声明和调用。我的语言学习过程一般分为下面几个:1. 变量和数据类型2. 流程控制方法3. 函数声明和调用4. 面向对象5. 语言特性6. 标准库函数声明func 函数名称(参数表) 返回值类型 { // 函数体}写一个函数是非常简单的,掌握语法格式就可以了。函数是一个功能的封装,能让函数体内的代码得到很好的复用。比如我要输出个人信息,我可以把个人信息封装到函数里面,后续直接调用这个函数而不是每次都print一堆信息了上面定义的函数没有参数,也没有返回值,非常简单的一个函数。如果我想让姓名可变,那么可以定义一个带有参数的函数接下来定义一个有

C++动态内存管理

C++中,动态内存管理是通过一对运算符来完成:new 和 delete。new操作符在内存中为对象分配空间并返回一个指向该对象的指针,delete接收一个动态对象的指针,销毁该对象,并释放与之相关的内存。手动管理内存看起来只有这两个操作,似乎很轻松,但实际上这是一件非常繁琐的事情,分配了内存但没有释放内存的场景发生的概率太大了!回想一下,你有多少次打开抽屉却没关上,拿出来的护肤品擦完脸之后却忘了放回去,吃完饭却忘了洗碗。类似这种没有收尾的事情我做的太多了。(以上这些都是在实际生活中我爱人批评我的点)我连这种明面上的事情都能忘记收尾,何况分配内存!所以为了世界和平,我放弃了手动管理内存。好在C+

C++ 判断 char* 是否相等

在C++中, 是一个指向字符的指针,通常用于表示C风格的字符串。判断两个  指针是否相等,需考虑两个方面:判断指针本身的地址是否相等:可以通过直接使用  或  操作符来实现。 和  指向不同的地址(尽管它们的内容相同),而  和  指向相同的地址。2. 判断指针指向的字符串内容是否相等:需要使用  函数,它是C标准库中的一部分,在C++中可用。 函数用于比较 、 和  的内容。如果内容相同, 返回

python学习笔记:三、函数

这是第三篇python学习笔记,我们即将要学习python的函数。内容主要包括两个部分,函数的声明和函数的调用。函数声明和调用比如我们要声明一个“吃”的函数,语法如下:def eat(): return "eat something"print(eat())上面是一个没有参数的函数,做的事情很简单,声明一个函数,然后返回一个字符串。接下来要增加一个参数了。def ead(food): return "eat %s" % foodprint(eat('fruit'))可以看到,上面声明了一个带有一个参数的函数,当然可以声明带两个,三个等。这些都是固定的,那么如果要声明一个不固定参数的

Docker保存当前容器镜像

当我们在容器里面安装了一些软件或者修改一些设置之后,我们希望能把修改同步到相关的镜像,下次用这个镜像生成容器的时候,可以略过搭建环境这一步了。比如我现在有一个容器叫centos进入容器安装一些软件之后,通过 命令将我们的修改保存的新的镜像。上面的镜像名称我加了一个标签。

C++入门:一、变量和数据类型

这是我的C++学习笔记第一篇,同所有的程序语言学习路径一样,首先学习的是变量和数据类型。我的学习路径如下:1. 变量和数据类型2. 流程控制3. 函数声明和调用4. 面向对象5. 标准库这一章,学习的是变量和数据类型,需要了解的有:了解这些,对于变量基本就够了。Hello world在开始之前,先写一个hello world来熟悉一下程序的主要结构以及如何打印一个变量。iostream提供标准输入输出的头文件,程序以main函数问入口,std为标准库的命名空间,“<<” 为输出操作符,std::cout为标准输出,std::endl为结束符,表示将等待输出的内容从内存传送到标准输出

还能这样?把 Python 自动翻译成 C++

一、问题背景随着深度学习的广泛应用,在搜索引擎/推荐系统/机器视觉等业务系统中,越来越多的深度学习模型部署到线上服务。机器学习模型在离线训练时,一般要将输入的数据做特征工程预处理,再输入模型在 TensorFlow PyTorch 等框架上做训练。1.常见的特征工程逻辑常见的特征工程逻辑有: 分箱/分桶 离散化 log/exp 对数/幂等 math numpy 常见数学运算 特征缩放/归一化/截断 交叉特征生成 分词匹配程度计算 字符串分隔匹配判断 tong 缺省值填充等 数据平滑 onehot 编码,hash 编码等这些特征工程代码,当然一般使用深度学习最主要的语言 pyt

赞赏

微信赞赏支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注