提问人:zrli 提问时间:11/5/2023 最后编辑:JaMiTzrli 更新时间:11/5/2023 访问量:234
为什么 C++ 映射说结构存在,而不存在?
Why Does C++ Map Say That Struct Exists When Doesn't?
问:
我有一个名为 foo 的结构,还有一个名为 mp 的映射,但出于某种原因,当我插入 {5, 0, 3} 并查询 {5, 3, 0} 时,映射说它包含 {5, 3, 0},而实际上它没有:
#include <bits/stdc++.h>
using namespace std;
struct foo {
int v1, v2, v3;
friend bool operator<(const foo &a, const foo &b) {
return a.v1 < b.v1;
}
friend bool operator==(const foo &a, const foo &b) {
return (a.v1 == b.v1) && (a.v2 == b.v2) && (a.v3 == b.v3);
}
};
int main() {
map<foo, int> mp;
mp[{5, 0, 3}] = 1;
if(mp[{5, 3, 0}]) {
cout << "YES\n";
} else {
cout << "NO\n";
}
return 0;
}
输出:
YES
有谁知道为什么我的代码会这样做?如果是这样,我需要更改什么?
答:
6赞
JaMiT
11/5/2023
#1
比较运算符与相等运算符不匹配。A 不使用相等运算符;它使用“小于”。如果两者都不是,则地图会考虑和等效。在您的情况下:std::map
a < b
b < a
a
b
foo{5, 0, 3} < foo{5, 3, 0}
是因为是假的。false
5 < 5
foo{5, 3, 0} < foo{5, 0, 3}
是因为是假的。false
5 < 5
因此,地图认为这些对象是等价的。因此,找到您刚刚设置为 1 的 map 元素,因此它是一个真实值。mp[{5, 3, 0}]
为了得到你想要的结果,你需要考虑所有三个成员,而不仅仅是.operator<
v1
例如:
friend bool operator<(const foo &a, const foo &b) {
if (a.v1 != b.v1)
return a.v1 < b.v1;
if (a.v2 != b.v2)
return a.v2 < b.v2;
return a.v3 < b.v3;
}
或者(正如PaulMcKenzie所建议的)更简单地说:
friend bool operator<(const foo &a, const foo &b) {
return std::tie(a.v1, a.v2, a.v3) < std::tie (b.v1, b.v2, b.v3);
}
评论
0赞
zrli
11/5/2023
我将如何做到这一点,以便将所有三个成员都占为止?由于我刚刚尝试了这个,它至少返回了正确的答案,但仍然有某种导致 TLE 的问题: struct foo { int v1, v2, v3; friend bool operator<(const foo &a, const foo &b) { if(a.v1 < b.v1) { return true; else if(a.v2 < b.v2) { return true<;
0赞
PaulMcKenzie
11/5/2023
@zrli -- 在主要评论部分查看我的评论。
0赞
zrli
11/5/2023
@PaulMcKenzie OK
0赞
JaMiT
11/5/2023
@zrli -- 当 和 其他字段的值导致运算符返回 时,就会发生此处的错误。使用(正如 PaulMcKenzie 所建议的)可以让你不必记住这个比较函数可能出错的所有方式(以及其他人已经遇到过数百次)的方式。if(a.v1 < b.v1) { return true; } else if(a.v2 < b.v2) { return true; } else if(a.v3 < b.v3) { return true; } return false;
a.v1 > b.v1
true
std:tie
0赞
zrli
11/5/2023
@JaMiT是的,这个问题现在已经解决了!您是否偶然知道结构体是否比元组更耗时?
2赞
gbjbaanb
11/5/2023
#2
int v1, v2, v3;
friend bool operator<(const foo &a, const foo &b) {
return a.v1 < b.v1;
}
首先,这只检查第一个值。所以 5,0,0 将被视为相同的 a 5,9,9 等。
if(mp[{5, 3, 0}])
这与你的想法不同。如果不存在,Operator[] 将创建一个新条目。但是,当它插入值 0 时,您的 if 语句给出了您期望的结果,但它没有按照您期望的方式执行。这种“副作用”很糟糕,这就是错误蔓延的地方。您应该改用 map.find() 之类的方法
评论
mt19937_64 rng(std::random_device{}());
operator<
仅检查 so 并且是等效的。 从未使用过。v1
5,0,3
5,3,0
operator==