How can I use std::maps with user-defined types as key?
我想知道为什么我不能将STL映射用于用户定义的类。当我编译下面的代码时,会收到下面的错误消息。这是什么意思?另外,为什么它只发生在用户定义的类型上?(当基元类型用作键时,它们是可以的。)
C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_function.h||In
member function `bool
std::less<_Tp>::operator()(const _Tp&,
const _Tp&) const [with _Tp =
Class1]':|C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_map.h|338|instantiated
from `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = Class1, _Tp = int, _Compare = std::less, _Alloc = std::allocator >]'|C:\Users\Admin\Documents\dev\sandbox\sandbox\sandbox.cpp|24|instantiated
from here|C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_function.h|227|error: no match for 'operator<' in '__x < __y'| ||=== Build finished: 1 errors, 0 warnings ===|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <iostream> #include <map> using namespace std; class Class1 { public: Class1(int id); private: int id; }; Class1::Class1(int id): id(id) {} int main() { Class1 c1(1); map< Class1 , int> c2int; c2int[c1] = 12; return 0; } |
实际上,您不必为您的类定义
1 2 3 4 5 6 7 8 9 | struct Class1Compare { bool operator() (const Class1& lhs, const Class1& rhs) const { return lhs.id < rhs.id; } }; std::map<Class1, int, Class1Compare> c2int; |
正好相反,
实现这一点的另一种方法是专门化
1 2 3 4 5 6 7 8 9 10 | namespace std { template<> struct less<Class1> { bool operator() (const Class1& lhs, const Class1& rhs) const { return lhs.id < rhs.id; } }; } |
这样做的好处是,它将由
默认情况下,
两个对象被认为是等价的
如果出于某种原因,您想使用不同的比较器,那么可以将
您需要为Class1定义
map需要使用operator<比较值,因此当用户定义的类用作键时,需要提供相同的值。
1 2 3 4 5 6 7 8 9 10 11 12 | class Class1 { public: Class1(int id); bool operator <(const Class1& rhs) const { return id < rhs.id; } private: int id; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class key { int m_value; public: bool operator<(const key& src)const { return (this->m_value < src.m_value); } }; int main() { key key1; key key2; map<key,int> mymap; mymap.insert(pair<key,int>(key1,100)); mymap.insert(pair<key,int>(key2,200)); map<key,int>::iterator iter=mymap.begin(); for(;iter!=mymap.end();++iter) { cout<<iter->second<<endl; } } |
密钥必须是可比较的,但您还没有为自定义类定义合适的
我想对帕维尔·米纳耶夫的答案做一点扩展,在阅读我的答案之前,你应该先阅读一下。如果要比较的成员(如问题代码中的
error C2248: 'Class1::id' : cannot access private member declared in class 'Class1'
正如天行者在关于帕维尔答案的评论中所提到的,使用
1 2 3 4 5 6 7 8 9 10 | class Class1 { public: Class1(int id) : id(id) {} private: int id; friend struct Class1Compare; // Use this for Pavel's first solution. friend struct std::less<Class1>; // Use this for Pavel's second solution. }; |
Ideone代码
但是,如果您的私人成员具有访问功能,例如
1 2 3 4 5 6 7 8 9 | class Class1 { public: Class1(int id) : id(id) {} int getId() const { return id; } private: int id; }; |
然后您可以使用它而不是
1 2 | auto comp = [](const Class1& lhs, const Class1& rhs){ return lhs.getId() < rhs.getId(); }; std::map<Class1, int, decltype(comp)> c2int(comp); |
Ideone代码
正确的解决方案是为您的类/结构专门化
?基本上,cpp中的映射被实现为二进制搜索树。
For each node, node.left.key < node.key < node.right.key
BST中的每个节点都包含元素,如果映射了元素的键和值,则应该对键进行排序。关于映射实现的更多信息:映射数据类型。
对于cpp映射,键是节点的元素,值不参与树的组织,它只是一个补充数据。
因此,这意味着密钥应该与
否则,如果您使用用户定义的数据类型作为键,那么需要为该数据类型提供意义完整的比较语义。
解决方案:专攻
地图模板中的第三个参数是可选的,它是
因此,为用户定义的数据类型创建一个新的
1 2 3 4 5 6 7 8 9 10 11 | namespace std { template<> struct less<MyClass> { bool operator() (const MyClass& lhs, const MyClass& rhs) const { return lhs.anyMemen < rhs.age; } }; } |
注意:您需要为每个用户定义的数据类型(如果您想将该数据类型用作CPP映射的键)创建专用的
不良解决方案:为用户定义的数据类型重载
请检查答案Pavel Minaev的答案