我想知道为什么我不能将STL映射与用户定义的类一起使用。当我编译下面的代码时,我收到以下神秘的错误消息。这是什么意思?此外,为什么它只发生在用户定义的类型? (当它们用作密钥时,原始类型是可以的。)
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 |错误:不匹配'运算符<'in'__ 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;
}
实际上,您不必为您的班级定义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;
恰好,std::map
的第三个模板参数的默认值是std::less
,它将委托给你的类定义的operator<
(如果没有,则失败)。但有时你希望对象可以用作地图键,但你实际上并没有任何有意义的比较语义,因此你不想通过在你的类上提供operator<
来混淆人们。如果是这种情况,您可以使用上述技巧。
实现同样目标的另一种方法是专门化std::less
:
namespace std
{
template<> struct less<Class1>
{
bool operator() (const Class1& lhs, const Class1& rhs) const
{
return lhs.id < rhs.id;
}
};
}
这样做的好处是它将被std::map
“默认”选中,但你不会将operator<
暴露给客户端代码。
默认情况下,std::map
(和std::set
)使用operator<
来确定排序。因此,您需要在类上定义operator<
。
两个对象被认为是equivalent if !(a < b) && !(b < a)
。
如果出于某种原因,你想使用不同的比较器,可以将map
的第三个模板参数更改为std::greater
。
您需要为Class1定义operator <
。
Map需要使用operator <来比较值,因此当用户定义的类用作键时,您需要提供相同的值。
class Class1
{
public:
Class1(int id);
bool operator <(const Class1& rhs) const
{
return id < rhs.id;
}
private:
int id;
};
键必须具有可比性,但您尚未为自定义类定义合适的operator<
。
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;
}
}
我想在Pavel Minaev的answer上稍微扩展一下,在阅读我的答案之前你应该阅读。如果要比较的成员(例如问题代码中的id
)是私有的,Pavel提出的两个解决方案都不会编译。在这种情况下,VS2013会为我抛出以下错误:
错误C2248:'Class1 :: id':无法访问类'Class1'中声明的私有成员
正如SkyWalker在关于Pavel的答案中的comments所提到的那样,使用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.
};
但是,如果您的私有成员具有访问权限功能,例如getId()
用于id
,如下所示:
class Class1
{
public:
Class1(int id) : id(id) {}
int getId() const { return id; }
private:
int id;
};
然后你可以使用它而不是friend
声明(即你比较lhs.getId() < rhs.getId()
)。从C++11开始,你也可以使用lambda expression作为Pavel的第一个解决方案,而不是定义比较器函数对象类。将所有内容放在一起,代码可以如下所示:
auto comp = [](const Class1& lhs, const Class1& rhs){ return lhs.getId() < rhs.getId(); };
std::map<Class1, int, decltype(comp)> c2int(comp);
正确的解决方案是为您的班级/结构专业化std::less
。
•基本上cpp中的映射实现为二进制搜索树。
对于每个节点,node.left.key <node.key <node.right.key
BST中的每个节点都包含元素,并且在映射的情况下,它的KEY和值,并且键应该被排序。关于地图实施的更多信息:The Map data Type。
在cpp映射的情况下,键是节点的元素,值不参与树的组织,它只是一个补充数据。
所以这意味着键应该与std::less
或operator<
兼容,以便它们可以组织起来。请检查map parameters。
否则,如果您使用用户定义的数据类型作为键,则需要为该数据类型提供含义完全比较语义。
解决方案:专业化std::less
:
地图模板中的第三个参数是可选的,它是std::less
,它将委托给operator<
,
因此,为您的用户定义的数据类型创建一个新的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;
}
};
}
注意:您需要为每个用户定义的数据类型创建专门的std::less
(如果要将该数据类型用作cpp映射的键)。
错误解决方案:为您的用户定义数据类型重载operator<
。这个解决方案也可以工作但非常糟糕,因为运算符<
将为您的数据类型/类普遍地重载。这在客户端场景中是不可取的。