C++ 如何使用带有用户定义类型的 std::maps 作为键?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1102392/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I use std::maps with user-defined types as key?
提问by unknown
I'm wondering why I can't use STL maps with user-defined classes. When I compile the code below, I get the following cryptic error message. What does it mean? Also, why is it only happening with user-defined types? (Primitive types are okay when they are used 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 ===|
C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_function.h||在成员函数`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|从`_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|从这里实例化|
C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_function.h|227|错误:“运算符不匹配” <' 在'__x < __y'| ||=== 构建完成:1 个错误,0 个警告 ===|
#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;
}
回答by Pavel Minaev
You don't haveto define operator<
for your class, actually. You can also make a comparator function object class for it, and use that to specialize std::map
. To extend your example:
你不具备定义operator<
为类,其实。您还可以为它创建一个比较器函数对象类,并使用它来专门化std::map
. 扩展您的示例:
struct Class1Compare
{
bool operator() (const Class1& lhs, const Class1& rhs) const
{
return lhs.id < rhs.id;
}
};
std::map<Class1, int, Class1Compare> c2int;
It just so happens that the default for the third template parameter of std::map
is std::less
, which will delegate to operator<
defined for your class (and fail if there is none). But sometimes you want objects to be usable as map keys, but you do not actually have any meaningfulcomparison semantics, and so you don't want to confuse people by providing operator<
on your class just for that. If that's the case, you can use the above trick.
碰巧的std::map
是std::less
,第三个模板参数的默认值为,它将委托operator<
为您的类定义(如果没有,则失败)。但有时您希望对象可用作映射键,但您实际上没有任何有意义的比较语义,因此您不想通过operator<
在您的类中提供来混淆人们。如果是这种情况,您可以使用上述技巧。
Yet another way to achieve the same is to specialize std::less
:
实现相同目标的另一种方法是专门化std::less
:
namespace std
{
template<> struct less<Class1>
{
bool operator() (const Class1& lhs, const Class1& rhs) const
{
return lhs.id < rhs.id;
}
};
}
The advantage of this is that it will be picked by std::map
"by default", and yet you do not expose operator<
to client code otherwise.
这样做的好处是它将被std::map
“默认”选择,但您不会operator<
以其他方式暴露给客户端代码。
回答by GManNickG
By default std::map
(and std::set
) use operator<
to determine sorting. Therefore, you need to define operator<
on your class.
默认情况下std::map
(和std::set
)用于operator<
确定排序。因此,您需要operator<
在您的班级上进行定义。
Two objects are deemed equivalentif !(a < b) && !(b < a)
.
两个对象被认为是等效的if !(a < b) && !(b < a)
。
If, for some reason, you'd like to use a different comparator, the third template argument of the map
can be changed, to std::greater
, for example.
如果出于某种原因,您想使用不同的比较器,则map
可以将的第三个模板参数更改为std::greater
,例如。
回答by aJ.
You need to define operator <
for the Class1.
您需要operator <
为 Class1定义。
Map needs to compare the values using operator < and hence you need to provide the same when user defined class are used as key.
Map 需要使用运算符 < 比较值,因此当用户定义的类用作键时,您需要提供相同的值。
class Class1
{
public:
Class1(int id);
bool operator <(const Class1& rhs) const
{
return id < rhs.id;
}
private:
int id;
};
回答by unwind
Keys must be comparable, but you haven't defined a suitable operator<
for your custom class.
键必须具有可比性,但您尚未定义适合operator<
您的自定义类的键。
回答by Kaushal
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;
}
}
回答by honk
I'd like to expand a little bit on Pavel Minaev'sanswer, which you should read before reading my answer. Both solutions presented by Pavel won't compile if the member to be compared (such as id
in the question's code) is private. In this case, VS2013 throws the following error for me:
我想稍微扩展一下Pavel Minaev 的回答,您应该在阅读我的回答之前先阅读这些内容。如果要比较的成员(例如id
在问题的代码中)是私有的,Pavel 提出的两种解决方案都不会编译。在这种情况下,VS2013 为我抛出以下错误:
error C2248: 'Class1::id' : cannot access private member declared in class 'Class1'
错误 C2248:“Class1::id”:无法访问类“Class1”中声明的私有成员
As mentioned by SkyWalkerin the commentson Pavel'sanswer, using a friend
declaration helps. If you wonder about the correct syntax, here it is:
正如SkyWalker在对Pavel回答的评论中所提到的,使用friend
声明会有所帮助。如果你想知道正确的语法,这里是:
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.
};
However, if you have an access function for your private member, for example getId()
for id
, as follows:
但是,如果您有私有成员的访问功能,例如getId()
for id
,如下所示:
class Class1
{
public:
Class1(int id) : id(id) {}
int getId() const { return id; }
private:
int id;
};
then you can use it instead of a friend
declaration (i.e. you compare lhs.getId() < rhs.getId()
).
Since C++11, you can also use a lambda expressionfor Pavel'sfirst solution instead of defining a comparator function object class.
Putting everything together, the code could be writtem as follows:
那么你可以使用它而不是friend
声明(即你比较lhs.getId() < rhs.getId()
)。从C++11 开始,您还可以将lambda 表达式用于Pavel 的第一个解决方案,而不是定义比较器函数对象类。把所有的东西放在一起,代码可以写成如下:
auto comp = [](const Class1& lhs, const Class1& rhs){ return lhs.getId() < rhs.getId(); };
std::map<Class1, int, decltype(comp)> c2int(comp);
回答by BreakBadSP
The right solution is to Specialize std::less
for your class/Struct.
正确的解决方案是std::less
针对您的类/结构进行专业化。
? Basically maps in cpp are implemented as Binary Search Trees.
? cpp 中的映射基本上是作为二叉搜索树实现的。
- BSTs compare elements of nodes to determine the organization of the tree.
- Nodes who's element compares less than that of the parent node are placed on the left of the parent and nodes whose elements compare greater than the parent nodes element are placed on the right. i.e.
- BST 比较节点的元素以确定树的组织。
- 元素比较小于父节点的节点放在父节点的左边,元素比较大于父节点元素的节点放在右边。IE
For each node, node.left.key < node.key < node.right.key
对于每个节点,node.left.key < node.key < node.right.key
Every node in the BST contains Elements and in case of maps its KEY and a value, And keys are supposed to be ordered. More About Map implementation : The Map data Type.
BST 中的每个节点都包含元素,并且在映射的情况下,它的 KEY 和一个值,并且键应该是有序的。更多关于地图实现: 地图数据类型。
In case of cpp maps , keys are the elements of the nodes and values does not take part in the organization of the tree its just a supplementary data .
在 cpp 映射的情况下,键是节点的元素,值不参与树的组织,它只是一个补充数据。
So It means keys should be compatible with std::less
or operator<
so that they can be organized. Please check map parameters.
所以这意味着密钥应该与std::less
或兼容,operator<
以便它们可以被组织。请检查地图参数。
Else if you are using user defined data type as keys then need to give meaning full comparison semantics for that data type.
否则,如果您使用用户定义的数据类型作为键,则需要为该数据类型提供完整的比较语义。
Solution: Specialize std::less
:
解决方案:专业化std::less
:
The third parameter in map template is optional and it is std::less
which will delegate to operator<
,
地图模板中的第三个参数是可选的std::less
,它将委托给operator<
,
So create a new std::less
for your user defined data type. Now this new std::less
will be picked by std::map
by default.
因此,std::less
为您的用户定义的数据类型创建一个新 的。现在默认情况下std::less
会选择这个新的std::map
。
namespace std
{
template<> struct less<MyClass>
{
bool operator() (const MyClass& lhs, const MyClass& rhs) const
{
return lhs.anyMemen < rhs.age;
}
};
}
Note: You need to create specialized std::less
for every user defined data type(if you want to use that data type as key for cpp maps).
注意:您需要std::less
为每个用户定义的数据类型专门创建(如果您想使用该数据类型作为 cpp 映射的键)。
Bad Solution:Overloading operator<
for your user defined data type.
This solution will also work but its very bad as operator <
will be overloaded universally for your data type/class. which is undesirable in client scenarios.
错误的解决方案:重载operator<
用户定义的数据类型。这个解决方案也可以工作,但它非常糟糕,因为运算符<
会为您的数据类型/类普遍重载。这在客户端场景中是不可取的。
Please check answer Pavel Minaev's answer
请检查答案Pavel Minaev 的答案