具有覆盖运算符或外部仿函数的可散列类型

Hashable type with overwritten operators or external functors

要在 std::unordered_set 中使用自定义类型,我必须选择。

1) 为我的类型实现 == 运算符并特化 std::hash

struct MyType {
    int x;

    bool operator==(const MyType& o) {
        return this.x == o.x;
    }
};

namespace std
{
template<>
struct hash<MyType> {
    size_t operator()(const MyType& o) const {
        return hash<int>()(o.x);
    }
};
}

std::unordered_set<MyType> mySet;

或2),提供函子类:

struct MyTypeHash {
    size_t operator()(const MyType& o) const {
        return std::hash<int>()(o.x);
    }
};

struct MyTypeCompare {
  bool operator()(const MyType& o1, const MyType& o2) const {
    return o1.x == o2.x;
  }
};

std::unordered_set<MyType, MyTypeHash, MyTypeCompare> mySet;

第二种方法让我为 std::unordered_set 的每个新实例选择新的行为,而第一种方法作为类型本身的一部分的行为将始终相同。

现在,如果我知道我只想要一个行为(我永远不会为 MyType 定义两个不同的比较器),那么首选哪种方法?这两者之间还有哪些其他区别?

将行为附加到类型允许像

这样的代码
template<template<class> Set,class T>
auto organizeWithSet(…);

/* elsewhere */ {
  organizeWithSet<std::unordered_set,MyType>(…);
  organizeWithSet<std::set,MyType>(…);
}

显然不能传递自定义函数对象

也就是说,可以定义

template<class T>
using MyUnorderedSet=std::unordered_set<T, MyTypeHash,MyTypeCompare>;

并将其用作模板模板参数,尽管这引入了另一个名称并且可能被认为可读性较差。

否则,您必须考虑到您的 operator== 同时是 std::unordered_setstd::find 等的默认设置;如果出于这些目的您想要的等价物有所不同,您可能需要命名比较器。另一方面,如果一个就足够了,C++20 甚至可以让您仅用 =default.

来定义它