2

假设我有以下简单程序(http://cpp.sh/5sygh):

#include <map>
#include <iostream>

using Key = std::pair<unsigned long, unsigned long long>;

struct KeyLess {
  bool operator()(const Key& lhs, const Key& rhs) {
      if (lhs.first < rhs.first) {
        return  true;
      }

      if (lhs.second < rhs.second) {
        return true;
      }

      return false;
  }
};

int main() {
    std::map< Key , int, KeyLess> m;
    m[Key{2, 169}] = 1;
    m[Key{1, 255}] = 2;
    m[Key{1, 391}] = 3;
    m[Key{1, 475}] = 4;

    std::cout << "Elements in map: " << m.size() << std::endl;     
    for(const auto &x: m) {
        std::cout <<"Value: "<< x.second << std::endl;
    }
}

输出仅包含 2 个项目,而不是地图中的 4 个:

Elements in map: 4
Value: 2
Value: 1

我在这里想念什么?

4

5 回答 5

8

您的 less 运算符应该是:

struct KeyLess {
  bool operator()(const Key& lhs, const Key& rhs) {
      if (lhs.first < rhs.first) {
        return  true;
      }

      if (lhs.first == rhs.first && lhs.second < rhs.second) {
        return true;
      }

      return false;
  }
};

当您将结构与多个元素进行比较时,将结构视为单词并将元素视为字符可能会有所帮助。

通过此修改,less 运算符按字典顺序工作,即在对它们进行排序时比较两个相同长度的单词的方式:您在下一个位置继续比较,而单词在当前位置具有相同的字符,并确定字符何时位于当前位置不同。如果您到达两个单词的末尾,则单词相等。

于 2020-03-18T06:38:01.457 回答
7

您的比较功能不符合严格弱排序的要求。

在 SWO 中,如果 A < B 且 B < C,则 A 必须小于 C。还通过查看两个值是否彼此不小于来检查密钥是否相等。如果(!(a<b) && !(b<a))那么a == b. 两个键不应都小于彼此。

对于您的密钥并使用您的比较功能

Key{2, 169} < Key{1, 255} // this is true because 169 < 255
Key{1, 255} < Key{2, 169} // this is also true because 1 < 2

显然这是一个问题,因为这两个键使用比较器进行的比较少于彼此。

我建议的解决方案:由于您的键是std::pairs,因此您不需要定义新的比较器。std::pair默认情况下已经使用字典比较。

于 2020-03-18T06:43:44.617 回答
7

您可以隐藏比较器的复杂性并通过使用std::tie.

bool operator()(const Key& lhs, const Key& rhs)
{
   return std::tie(lhs.first, lhs.second) < std::tie(rhs.first, rhs.second);
}
于 2020-03-18T06:44:02.410 回答
4

您的比较器不符合 的要求std::map,它需要提供严格的弱排序std::tuple如果您需要比较多个值,幸运的是为您实现了这一点:

struct KeyLess {
  bool operator()(const Key& lhs, const Key& rhs) {
      return std::tie(lhs.first, lhs.second) < std::tie(rhs.first, rhs.second);
  }
};

在您的情况下,您实际上根本不需要自定义比较器,因为std::pair'<运算符已经具有相同的行为。

于 2020-03-18T06:47:14.880 回答
0

您的KeyLess 代码导致不正确的比较:

KeyLess cmp;
std::cout << cmp(Key{2, 169},Key{1, 391})<< std::endl;  // yields true
std::cout << cmp(Key{1, 391},Key{2, 169})<< std::endl;  // yields true

当两个比较都产生 false 时,这意味着键相等,当它们产生 true 时,映射迭代器的行为没有定义。这与 map 对其元素进行排序这一事实有关。

请注意,operator()required to beconst或程序可能无法使用标准 C++17 及更高版本进行编译。作为一种可能的变体:

#include <map>
#include <iostream>

using Key = std::pair<unsigned long, unsigned long long>;

struct KeyLess {
  bool operator()(const Key& lhs, const Key& rhs) const {
      if (lhs.first < rhs.first) {
        return  true;
      }
      else  if (lhs.first > rhs.first)
          return  false;

      if (lhs.second < rhs.second) {
        return true;
      }

      return false;
  }
};


int main() 
{
    std::map< Key , int, KeyLess > m;
    m[Key{2, 169}] = 1;
    m[Key{1, 255}] = 2;
    m[Key{1, 391}] = 3;
    m[Key{1, 475}] = 4;

    std::cout << "Elements in map: " << m.size() << std::endl;     
    for(const auto &[key, value]: m) {
        std::cout << "Key: " << key.first << ", " << key.second << " Value: "<< value << std::endl;
    }
}
于 2020-03-18T07:05:17.060 回答