簡體   English   中英

std::map 帶有沒有排序的鍵

[英]std::map with keys that have no ordering

如果我想要使用用戶定義的對象作為鍵的std::map應該采用哪種方法?

讓我們考慮這個最小的虛假代碼(它可以編譯但不能正確運行):

#include <map>

class Object
{
public:
  int x, y;
  Object(int x, int y) : x(x), y(y) {};
  bool operator<(const Object& o) const { return 1;};   // bogus just to make compiler happy
};

int main()
{
  Object o1(1, 2), o2(3, 4);
  std::map<Object, int> objmap;
  objmap.insert(std::make_pair(o1, 11));
  objmap.insert(std::make_pair(o2, 22));
}

為了能夠在std::map使用對象作為鍵,我們需要一個<操作符。

到目前為止一切都很好,但是如果不能用“更少”或“更大”來比較對象,而只能用“不同”來比較,那該怎么辦?

您的對象必須遵守嚴格的總順序 這意味着,最重要的是,您的<關系必須是非自反和可傳遞的。 在您的情況下,請考慮僅使用 std::tuple,它已經提供了一個具有字典順序的功能operator<

這將是“一個”正確的排序,但很可能根本不是您想要的:

friend bool operator<(Object const& a, Object const& b) {
  return a.x < b.x; // probably not what you want, but legal
}

因為這意味着Object{1,100}Object{1,101}會導致std::map假設它們是相同的鍵(除非你想要那個)。

但是,如果您提供以下內容,您實際上可以打破地圖:

friend bool operator<(Object const& a, Object const& b) {
  return a.x <= b.x; // this will break map
}

因為它不是非自反的(即 x < x 不會產生錯誤)。

通常,您必須考慮對象的所有屬性,以避免混疊(即 map 認為兩個不同的對象相等)。 除非您向我們展示您的Object ,否則無法向您展示如何實現正確的operator<


在大多數情況下, std::unordered_map是更好(也更有效)的選擇。

到目前為止一切都很好,但是如果不能用“更少”或“更大”來比較對象,而只能用“不同”來比較,那該怎么辦?

您一般可合成排序,如

bool operator<(const Object& o) const { return std::tie(a, b) < std::tie(o.a, o.b); }

順便說一下,這種構造是 C++20 的默認比較將使用的。

auto operator<=>(const Object& o) const = default;

由於這里Object之間沒有全序關系,一種可能性是使用unorded_map而不是map

因此object類至少需要一個重載==運算符和一個自定義哈希函數:

#include <unordered_map>
#include <iostream>

class Object
{
public:
  int x, y;
  Object(int x, int y) : x(x), y(y) {};

  bool operator==(const Object& o) const
  {
    std::cout << "equal hashes, using == operator on (" << x << "," << y << ")\n";
    auto isequal = x == o.x && y == o.y;
    std::cout << "objects are " << (isequal ? "" : "not ") << "equal\n";
    return isequal;
  };

  friend std::ostream& operator<<(std::ostream& os, const Object& o);
};

std::ostream& operator<<(std::ostream& os, const Object & o)
{
  os << "(" << o.x << "," << o.y << ")";
  return os;
}

struct ObjectHash {
  size_t operator()(const Object& o) const
  {
    std::cout << "hashing (" << o.x << "," << o.y << ")\n";;
    return o.x + o.y;  // purposly bad hash function, so we get collisions
                       // (o.x * 127) ^ o.y  would be better, still not ideal
  }
};


int main()
{
  Object o1(1, 2);
  Object o2(3, 4);  // different from o1, o3 and o4
  Object o3(1, 2);  // equal to o1
  Object o4(2, 1);  // different from o1, o2 and o3 but same hash as o1

  std::unordered_map<Object, int, ObjectHash> objmap;
  std::cout << "Inserting " << o1 << "\n"; objmap.insert(std::make_pair(o1, 11));
  std::cout << "Inserting " << o2 << "\n"; objmap.insert(std::make_pair(o2, 22));
  std::cout << "Inserting " << o3 << "\n"; objmap.insert(std::make_pair(o3, 33));
  std::cout << "Inserting " << o4 << "\n"; objmap.insert(std::make_pair(o4, 33));
}

std::cout用於說明目的。

輸出:

Inserting (1,2)
hashing (1,2)
Inserting (3,4)
hashing (3,4)
Inserting (1,2)
hashing (1,2)
equal hashes, using == operator on (1,2)
objects are equal
Inserting (2,1)
hashing (2,1)
equal hashes, using == operator on (2,1)
objects are not equal

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM