使用集合作为映射键的机制

Mechanism of using sets as keys to map

本文关键字:机制 映射 集合      更新时间:2023-10-16

我有这段代码,我不明白它为什么有效:

map<set<int>,int> states;
set<int> s1 = {5,1,3}, s2 = {1,5,3};    
states[s1] = 42;
printf("%d", states[s2]); // 42

输出为 42,因此状态键的值以某种方式用于比较。这怎么可能?我希望这不会像类似示例那样工作:

map<const char*,int> states;
char s1[]="foo", s2[]="foo";
states[s1] = 42;
printf("%d",states[s2]); // not 42

这里 char 指针的地址用作键,而不是它指向的内存值,对吧?请解释这两个样本之间的区别。

编辑:我刚刚发现了一些关于比较对象的东西,这解释了很多。但是如何为集合创建比较对象呢?我看不出它怎么可能是默认的较少对象。

你对比较对象做了一些事情。
C++ map的模板参数之一定义了什么充当比较谓词,默认情况下std::less<Key> ,在本例中为 std::less<set<int>>

从 cplusplus.com:

map 对象使用此表达式来确定元素在容器中遵循的顺序以及两个元素键是否等效(通过反射性比较它们:如果 !comp(a,b( &&!comp(b,a(,它们等效(。映射容器中没有两个元素可以具有等效的键。

std::less

二进制函数对象类,其调用返回其第一个参数是否小于第二个参数(由运算符 < 返回(。 std::set::key_comp : 默认情况下,这是一个 less 对象,它返回与 operator< 相同的内容。

现在,小于运算符有什么作用?

小于比较(operator<(的行为就像使用算法lexicographical_compare,它以倒数的方式依次使用operator<比较元素(即检查a<bb<a(并在第一次出现时停止。

或来自 MSDN:

集合对象之间的比较基于其元素的成对比较。两个对象之间的小于关系基于第一对不等元素的比较。

因此,由于这两个集合是等价的,因为set是按键排序的,因此使用任何一个 as 键都是指映射中的同一条目。
但是第二个示例使用指针作为键,因此就映射而言,两个等效值并不相等,因为在这种情况下operator<不是以任何特殊方式定义的,它只是地址之间的比较。如果您使用std::string作为键,它们会匹配(因为它们确实有自己的operator<(。

此行为的几个原因:

  • 集合在您输入值时保持排序,因此如果您将其打印出来,s1 和 s2 实际上看起来是一样的。
  • 集合具有重载运算符,因此 s1 == s2 将内容进行比较
  • 第二个带有 *char 的示例显式将指针(地址(作为键
  • 声明静态字符串为"foo"的两个实例提供不同的地址
    • 如果你把 s2 = s1 改为,你应该得到相同的行为

查看有关 cppreference.com 的文档:

template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T> >
> class map;

因此,如果您不提供自己的自定义比较函数,它将按<运算符对元素进行排序,并且:

operator==,!=,<,<=,>,>=(std::set)...
Compares the contents of lhs and rhs
lexicographically. The comparison is
performed by a function equivalent to 
std::lexicographical_compare.

因此,当您在两个集合上调用<时,它会按字典顺序比较它们的排序元素。 排序时,您的两组完全相同。

当你这样做时:

cout << (s1 == s2);

您获得的输出为 1 。我不知道C++的设置是如何工作的;我什至不知道它们是什么。我确实知道==比较运算符不会返回集合的值是否顺序相同。很可能,它返回集合是否包含相同的值,而不考虑顺序。

编辑:是的,集合已排序。因此,当您创建它们时,它们会被排序,这意味着它们成为同一件事。使用std::vectorstd::array

编辑#2:他们确实变得平等。让我打个比方。

int v1 = 4 + 1;
int v2 = 1 + 4;

现在很明显,v1 和 v2 将是相同的值。集合也不例外;创建后,内容将进行排序,当您使用 == 运算符进行比较时,这是std::map识别映射元素的部分方式,它会返回"是的,它们是相等的"。这就是为什么你的代码是如何工作的。