81

我想知道为什么我不能将 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|error: no match for 'operator <' 在 '__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;
}
4

8 回答 8

168

实际上,您不必为您的班级定义。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::mapstd::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<以其他方式暴露给客户端代码。

于 2009-07-09T09:11:21.417 回答
33

默认情况下std::map(和std::set)用于operator<确定排序。因此,您需要operator<在您的类上进行定义。

两个对象被认为是等价 if !(a < b) && !(b < a)的。

如果由于某种原因,您想使用不同的比较器,则map可以将 的第三个模板参数更改为std::greater,例如。

于 2009-07-09T07:44:26.303 回答
19

您需要operator <为 Class1 定义。

Map 需要使用运算符 < 比较值,因此当用户定义的类用作键时,您需要提供相同的值。

class Class1
{
public:
    Class1(int id);

    bool operator <(const Class1& rhs) const
    {
        return id < rhs.id;
    }
private:
    int id;
};
于 2009-07-09T07:44:23.853 回答
4
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;
    }


}
于 2015-11-06T15:44:09.263 回答
3

键必须具有可比性,但您尚未定义适合operator<您的自定义类。

于 2009-07-09T07:44:43.820 回答
3

我想稍微扩展一下Pavel Minaev 的 回答,您应该在阅读我的回答之前先阅读一下。id如果要比较的成员(例如在问题的代码中)是私有的,则 Pavel 提出的两种解决方案都不会编译。在这种情况下,VS2013 会为我抛出以下错误:

错误 C2248:“Class1::id”:无法访问在“Class1”类中声明的私有成员

正如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.
};

Ideone 上的代码

但是,如果您有私有成员的访问功能,例如getId()for id,如下所示:

class Class1
{
public:
    Class1(int id) : id(id) {}
    int getId() const { return id; }

private:
    int id;
};

那么你可以使用它而不是friend声明(即你比较lhs.getId() < rhs.getId())。从C++11开始,您还可以对Pavel 的第一个解决方案使用lambda 表达式,而不是定义比较器函数对象类。将所有内容放在一起,代码可以编写如下:

auto comp = [](const Class1& lhs, const Class1& rhs){ return lhs.getId() < rhs.getId(); };
std::map<Class1, int, decltype(comp)> c2int(comp);

Ideone 上的代码

于 2019-03-06T15:05:47.547 回答
2

如果您只是添加,您的示例适用于 C++20

auto operator<=>(Class1 const &) const = default;

到你的班级。

于 2021-12-11T23:43:45.220 回答
1

正确的解决方案是专门std::less针对您的类/结构。

• 基本上,cpp 中的地图是作为二叉搜索树实现的。

  1. BST 比较节点的元素以确定树的组织。
  2. 元素比较小于父节点的节点放置在父节点的左侧,元素比较大于父节点元素的节点放置在右侧。IE

对于每个节点,node.left.key < node.key < node.right.key

BST 中的每个节点都包含元素,在映射的情况下,它的 KEY 和一个值,并且键应该是有序的。更多关于地图实现: 地图数据类型

在 cpp 映射的情况下,键是节点的元素,值不参与树的组织,它只是一个补充数据。

所以这意味着键应该与它们兼容std::lessoperator<以便它们可以被组织。请检查地图参数

否则,如果您使用用户定义的数据类型作为键,则需要为该数据类型提供完整的比较语义。

解决方案:专业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<用户定义的数据类型。该解决方案也可以工作,但它非常糟糕,因为运算符<将普遍为您的数据类型/类重载。这在客户端场景中是不可取的。

请检查答案Pavel Minaev 的答案

于 2018-08-24T05:29:46.580 回答