我正在尝试使用简单的自定义键访问std :: map,但在大多数情况下这是有效的,每隔一段时间,根据给定的值,它将无法访问映射的值。
我在这里编写了一个测试程序,它更详细地显示了这个问题:
#include <map>
#include <cstdint>
#include <cassert>
struct key_type
{
uint32_t a;
uint32_t b;
bool operator<(const key_type& value) const
{
if (value.a < a)
return true;
if (value.b < b)
return true;
return false;
}
key_type(uint32_t a, uint32_t b) : a(a), b(b)
{}
};
std::map<key_type, int*> test;
int get_int(uint32_t a, uint32_t b)
{
if (test.count(key_type(a, b)) == 0)
{
int* r = new int;
assert(r != nullptr);
key_type key = key_type(a, b);
test[key] = r;
assert(test[key] != nullptr);
}
return *test[key_type(a,b)];
}
现在我尝试用两组不同的参数调用get_int。第一种情况按预期工作。
int main(int argc, char* argv[])
{
get_int(2, 4);
get_int(3, 4);
get_int(4, 5);
get_int(2, 1);
get_int(120, 1);
return 0;
}
现在如果我稍微改变一组值,一切都会爆炸。
int main(int argc, char* argv[])
{
get_int(2, 4);
get_int(3, 4);
get_int(4, 5);
get_int(120, 1);
return 0;
}
“assert(test [key]!= nullptr);”失败。虽然我可以规避实际的问题,但我想知道在这个表面下会发生什么导致这种行为?
答案 0 :(得分:6)
您的比较运算符没有多大意义。
的补充(value.a < a)
还包括value.a > a
。
如果你做了比较运算符的整个主体:
return std::make_pair(a, b) < std::make_pair(value.a, value.b);
更好的方法是使用std::tie
:
return std::tie(a, b) < std::tie(value.a, value.b);
答案 1 :(得分:2)
您的operator<
未强制执行严格的弱排序™。因此,您尝试使用map
是未定义的行为。
基本上,运营商实际上并没有产生一个订单来订购该类型的所有价值。
考虑:
bool operator<(const key_type& value) const
{
if (value.a != a)
return value.a < a;
if (value.b != b)
return value.b < b;
return false;
}
答案 2 :(得分:1)
您的订单弱得多。阅读此article from wikipedia和此one from Wolfram。
我希望您理解这些文章的重要性,但无论如何看,根据您的算法
(3,2) < (4,1) returns true
和
(4,1) < (3,2) returns true
std::map
需要强排序,上述操作会导致未定义的行为。
要解决此问题,您必须执行以下操作
if a < value.a return true;
if a > value.a return false;
if b < value.b return true;
return false;