使用自定义结构作为键类型访问std :: map会导致奇怪的行为

时间:2016-02-20 21:34:28

标签: c++ c++11 stl

我正在尝试使用简单的自定义键访问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);”失败。虽然我可以规避实际的问题,但我想知道在这个表面下会发生什么导致这种行为?

3 个答案:

答案 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;