0xzhang的博客

在容器中存放不同类型的变量

· 0xzhang

问题在于C++不是动态类型的语言,如何在运行时确定存储类型,参考知乎上的一些回答,了解到直接可以使用C++17支持的std::any/std::variant来进行对未知类型的存储。其它的一些方法基本会通过union来实现一个结构支持不同的类型。

std::any§

我试验了使用any的情况,发现基于any的实现可以很容易的存入容器比如vector<std::any>,但是当需要取出来时还是需要去对所有类型进行一个判断比较,使用std::any_cast去得到对应的类型。

#include <iostream>
#include <vector>
#include <any>
#include <string>
using namespace std;

int main()
{
	vector<any> anys;
	int a = 10;
	double b = 2.5;
	char c = 'f';
	string d = "Hello";

	anys.emplace_back(a);
	anys.emplace_back(b);
	anys.emplace_back(c);
	anys.emplace_back(d);

	for (const auto& t : anys) {
		if (t.type() == typeid(int)) {
			cout << "int:" << any_cast<int>(t) << endl;
		}
		else if (t.type() == typeid(double)) {
			cout << "double:" << any_cast<double>(t) << endl;
		}
		else if (t.type() == typeid(char)) {
			cout << "char:" << any_cast<char>(t) << endl;
		}
		else {
			cout << "string:" << any_cast<const string&>(t) << endl;
		}
	}
    return 0;
}

union§

再就是看到梁笔记博客上的实现方法,贴过来。

定义一个结构AnyData,结构中定义状态Eflag(用于判断是存的是什么类型)和union(里面存数据)。

结构AnyData定义构造函数,为每种类型定义一个。

getType( )用于获取当前数据类型。

getVal( )重载函数用于获取实际数据值。

用STL vector容器为例,实现存放不同类型完整代码:

// 梁笔记
// https://zouzhongliang.com
 
#include <iostream>
#include <vector>
using namespace std;
 
 
enum EType{E_bool, E_char, E_short, E_int, E_long,
			E_float, E_double};
 
 
struct AnyData
{
	EType Eflag;
	union {
		bool   bVal;
		char   cVal;
		short  sval;
		int    ival;
		long   lval;
		float  fval;
		double dval;
	};
 
	AnyData(bool val){
		Eflag = E_bool;
		bVal = val;
	}
	AnyData(char val){
		Eflag = E_char;
		cVal = val;
	}
	AnyData(short val){
		Eflag = E_short;
		sval = val;
	}
	AnyData(int val){
		Eflag = E_int;
		ival = val;
	}
	AnyData(long val){
		Eflag = E_long;
		lval = val;
	}
	AnyData(float val){
		Eflag = E_float;
		fval = val;
	}
	AnyData(double val){
		Eflag = E_double;
		dval = val;
	}
 
	EType getType(){
		return Eflag;
	}
 
 
	void getVal(bool& val){
		if (Eflag == E_bool)
			val = bVal;
	}
	void getVal(char& val){
		if (Eflag == E_char)
			val = cVal;
	}
	void getVal(short& val){
		if (Eflag == E_short)
			val = sval;
	}
	void getVal(int& val){
		if (Eflag == E_int)
			val = ival;
	}
	void getVal(long& val){
		if (Eflag == E_long)
			 val = lval;
	}
	void getVal(float& val){
		if (Eflag == E_float)
			val = fval;
	}
	void getVal(double& val){
		if (Eflag == E_double)
			val = dval ;
	}
 
	friend ostream& operator<<(ostream& out, AnyData& data);
 
};
 
ostream& operator<<(ostream& out, AnyData& data){
	switch(data.Eflag){
	case E_bool:
		out<< data.bVal;
		break;
 
	case E_char:
		out<< data.cVal;
		break;
 
	case E_short:
		out<< data.sval;
		break;
 
	case E_int:
		out<< data.ival;
		break;
 
	case E_long:
		out<< data.lval;
		break;
 
	case E_float:
		out<< data.fval;
		break;
 
	case E_double:
		out<< data.dval;
		break;
	}
	return out;
}
 
 
int main() {
 
	vector<AnyData> vecAnyData;
	vecAnyData.push_back(AnyData(false));
	vecAnyData.push_back(AnyData('q'));
	vecAnyData.push_back(AnyData((short)12));
	vecAnyData.push_back(AnyData(12));
	vecAnyData.push_back(AnyData((float)2.1));
	vecAnyData.push_back(AnyData(1.0));
 
 
	for(unsigned int i=0;i<vecAnyData.size();i++){
		cout<<vecAnyData[i]<<endl;
	}
 
	return 0;
}

输出结果:

0
q
12
12
2.1
1

总结§

通过以上两种实现是可以支持对任意的基本类型进行存放的,但不可避免的会有穷举判断类型的过程,对自定义类型,需要借助void*实现,在这里并没有介绍具体的过程,接触到具体情况再继续了解。

回到读取.csv文件的问题,通过以上两种方法能够支持对未知类型数据的存入,读出时再去类型枚举。对于首行列名即类型名进行解析是否有必要?建立一个类型序列,循环进行行的读取,但这样的问题是在存入时需要根据类型序列确定,存入到确定且相同类型的容器中,读出时也要根据类型序列确定类型。

参考资料§

[1] C++ 容器怎么存放不同类型的值? - 知乎

[2] C++17之std any_janeqi1987的专栏-CSDN博客

[3] STL容器中存放不同类型实现方法 – 梁笔记