C++实现任意类型键值存储的字典
初衷:由于程序开发,需要一个容器能带索引功能的存储lamada。通过索引来获取特定索引的lamda表达式。这然我想到了map。但是map的键值必须是同类型。于是有了想编写一个能存储不同类型lamada的map。说明:python中的数据结构允许存储不同类型的数据,这非常方便。但c++没有相关功能的容器。或许是本人见识太少,也许有。如果有还希望看此博文的各位前辈能告知一下小弟,在此表示感
初衷:
由于程序开发,需要一个容器能带索引功能的存储lamada。通过索引来获取特定索引的lamda表达式。这然我想到了map。但是map的键值必须是同类型。于是有了想编写一个能存储不同类型lamada的map。
说明:
python中的数据结构允许存储不同类型的数据,这非常方便。但c++没有相关功能的容器。或许是本人见识太少,也许有。如果有还希望看此博文的各位前辈能告知一下小弟,在此表示感谢!
不管怎样,已经萌生了要写这种容器的念头,也就难以姑息了。最开始的想法立马想到的就是c++泛型(template,auto)。但令人头痛的问题就是,无论是auto还是template都需要在编译的时候确定类型。否则无法编译通过。例如以下:
tempalte<typename T,typename U>
class Key
{
public:
T mKey;
U mVal;
}
在使用此类的时候,需要做如下声明:
Key<string,int> k;
而如果添加进入auto,例如:
class Key
{
public:
auto mKey;
auto mVal;
}
编译器无法确定变量类型,无法通过。
那假如将两者结合呢,有没有可能?
于是做了以下的辩想:
首先,要让一个容器能存储不同类型的变量,则其在声明的时候不能指定数值类型,例如Key<string,int>。但使用泛型是必须的,于是在声明容器类的时候,将template声明置于特定变量是合理的,例如:
class Key
{
template<typename T>
T mKey;
template<typename U>
U mVal;
}
但是以上的代码在编译的时候就已经过不了了:error C3857: 不允许使用多个 模板 参数列表。
有一个解决方法,就是不声明变量,转而声明成员函数,但这个函数必须类似于C#的属性一样,它必须实现对一个成员变量的取值和赋值。于是有了以下的声明:
class Key
{
public:
template<typename T>
auto mKey(T t)->decltype(T){return u;};
tepmlate<typename U>
auto mVal(U u)->decltype(U){return t;};
}
但在使用时无法编译:error C2893: 未能使函数模板“unknown-type Key::mKey(T)”专用化。以上是强迫症造成的,可以将auto去掉。
class Key
{
public:
template<typename T>
T mKey(T t){return t;};
tempalte<typename U>
U mVal(U u){return u};
}
这样,就可以不用在声明Key的时候指定类型了。但另一个麻烦的问题出现了,就是存储问题。编译器为Key开辟了一份内存,却没有开辟空间给存储数值,因为你没有声明用于存储的成员变量。无论是以下两种写法的一种,都无法使用:
一:
class Key
{
public:
template<typename T>
T mKey(T t){key=t;return key;};
T key;
template<typename U>
U mVal(U u){val=u;return val;};
U val;
}
二:
class Key
{
public:
template<typename T>
T mKey(T t){key=t;return key};
template<typename T>
T key;
template<typename U>
U mVal(U u){val=u;return val;};
template<typename U>
U val;
}
那么问题来了,如何存储变量,因为不能声明成员变量。有个解决的方法,就是利用lamada。lamada具有捕捉上文变量的功能,若将lamada用function类型来存储,就可以将上文中的变量存储在lamada之中。编译器为lamada声明了一份内存,同时其中也为上文中的变量声明了空间。这是好事。那么可以在Key类中声明两个function变量来存储lamada。
class Key
{
public:
Key(){};
private:
tr1::function<void* ()> mKey;
tr1::function<void* ()> mVal;
public:
template < typename U >
U& Key(U u){
U *b =& u;
mKey = [=]()->void*
{
return b;
};
return *((U*)mKey());
};
template < typename V >
V& Val(V t){
V *b = &t;
mVal = [=]()->void*
{
return b;
};
bValInit=true;
return *((V*)mVal());
};
};
这个类可以实现存储任何类型的变量,但有很多可以改进的地方,这里就不贴出改进的地方。只阐述了想法就可以了。再配合一下容器类就实现了一个map:
template<typename T>
class vMap
{
public:
vMap(void){};
~vMap(){};
void Add(vKey k){vKeys.push_back(k);};
vKey& operator[](T v){
for (int n = 0; n < vKeys.size(); n++)
{
if (vKeys[n].Key(v) == v)
return vKeys[n];
}
return NULLKEY;
};
private:
vector<vKey> vKeys;
};
注意到了我用于存储Key的是一个vector。在搜寻值时用了for循环。这样时间度就是omega(n)。这时一个线性递增的过程。后续会想提高效率。若有读者有好的想法,欢迎建议。谢谢!
更多推荐
所有评论(0)