如何使用以用户定义类型为键的 std::maps ?

发布于 2024-07-26 07:07:50 字数 1221 浏览 4 评论 0原文

我想知道为什么我不能将 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|错误:不匹配对于“运算符<” 在 '__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;
}

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.)

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 ===|

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

云柯 2024-08-02 07:07:50

实际上,您不必为您的类定义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< 暴露给客户端代码。

You don't have to 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:

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 meaningful comparison 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.

Yet another way to achieve the same is to specialize 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.

撩心不撩汉 2024-08-02 07:07:50

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

两个对象被视为等价 if !(a < b) & & !(b < a)

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

By default std::map (and std::set) use operator< to determine sorting. Therefore, you need to define operator< on your class.

Two objects are deemed equivalent 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.

睫毛上残留的泪 2024-08-02 07:07:50

您需要为 Class1 定义运算符 <

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

class Class1
{
public:
    Class1(int id);

    bool operator <(const Class1& rhs) const
    {
        return id < rhs.id;
    }
private:
    int id;
};

You need to define operator < for the 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.

class Class1
{
public:
    Class1(int id);

    bool operator <(const Class1& rhs) const
    {
        return id < rhs.id;
    }
private:
    int id;
};
奢望 2024-08-02 07:07:50
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;
    }


}
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;
    }


}
无人问我粥可暖 2024-08-02 07:07:50

您的示例可以在 C++20 中运行

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

如果您只是添加到您的类中,

Your example works in C++20 if you just add

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

to your class.

奈何桥上唱咆哮 2024-08-02 07:07:50

我想稍微扩展一下 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 上的代码

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

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

private:
    int id;
};

那么您可以使用它代替 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);

Code on Ideone

I'd like to expand a little bit on Pavel Minaev's answer, 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:

error C2248: 'Class1::id' : cannot access private member declared in class 'Class1'

As mentioned by SkyWalker in the comments on Pavel's answer, using a friend declaration helps. If you wonder about the correct syntax, here it is:

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

Code on Ideone

However, if you have an access function for your private member, for example getId() for id, as follows:

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 expression for Pavel's first solution instead of defining a comparator function object class.
Putting everything together, the code could be written as follows:

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

Code on Ideone

謌踐踏愛綪 2024-08-02 07:07:50

键必须具有可比性,但您尚未为自定义类定义合适的运算符<

Keys must be comparable, but you haven't defined a suitable operator< for your custom class.

若水微香 2024-08-02 07:07:50

正确的解决方案是为您的类/结构专门化std::less

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

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

对于每个节点,node.left.key < 节点.key < 节点.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 映射的键)。

错误的解决方案:
为您的用户定义的数据类型重载运算符<
这个解决方案也可以工作,但它非常糟糕,因为运算符 < 将为您的数据类型/类普遍重载。 这在客户端场景中是不可取的。

请检查答案

The right solution is to Specialize std::less for your class/Struct.

• Basically maps in cpp are implemented as Binary Search Trees.

  1. BSTs compare elements of nodes to determine the organization of the tree.
  2. 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.

For each node, 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.

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 .

So It means keys should be compatible with std::less or operator< so that they can be organized. Please check map parameters.

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:

The third parameter in map template is optional and it is std::less which will delegate to 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.

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).

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.

Please check answer Pavel Minaev's answer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文