您可以通过定义类型来覆盖散列和比较来实现 Abseil 的异构查找功能。根据文档,它们必须标有is_transparent
特征以支持转换。
struct VectorHash {
using is_transparent = void;
size_t operator()(absl::Span<int> v) const {
return absl::Hash<absl::Span<const int>>{}(v);
}
size_t operator()(const std::vector<int>& v) const {
return absl::Hash<absl::Span<const int>>{}(absl::Span<const int>{ v.data(), v.size() });
}
};
struct VectorEq {
using is_transparent = void;
bool operator()(const std::vector<int>& a, absl::Span<int> b) const {
return std::equal(a.begin(), a.end(), b.begin(), b.end());
}
bool operator()(absl::Span<int> b, const std::vector<int>& a) const {
return std::equal(a.begin(), a.end(), b.begin(), b.end());
}
bool operator()(const std::vector<int>& a, const std::vector<int>& b) const {
return std::equal(a.begin(), a.end(), b.begin(), b.end());
}
bool operator()(absl::Span<int> b, absl::Span<int> a) const {
return std::equal(a.begin(), a.end(), b.begin(), b.end());
}
};
using int_map_t = absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq>;
这将使查找使用at
或find
工作。但是[]
还是会失败。为什么?因为[]
运算符是一个 upsert - 如果它不存在,它会创建密钥。absl::string_view
有一个显式的转换运算符std::string
,因此,从一个工作中创建一个新std::string
键。absl::Span<int>
没有转换运算符 to std::vector<int>
,因此操作失败。
如果它不是使用at
而不是的选项[]
,您仍然可以扩展类型:
struct int_map_t : absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq> {
using absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq>::flat_hash_map;
using absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq>::operator [];
int& operator [](absl::Span<int> v) {
return operator [](std::vector<int> { v.begin(), v.end() });
}
};
演示:https ://godbolt.org/z/dW4av7
在评论中,您询问是否可以实现一个operator []
覆盖,如果地图条目存在,则不复制向量,同时仍然只做一个哈希。这有点 hacky 并且仍然可能进行额外的比较,但我认为您可以使用存储密钥和已计算哈希的辅助类型来完成此操作:
struct VectorHashMemo {
size_t hash;
absl::Span<int> key;
explicit operator std::vector<int>() const {
return { key.begin(), key.end() };
}
};
struct VectorHash {
/* ...existing overloads... */
size_t operator()(VectorHashMemo v) const {
return v.hash;
}
};
struct VectorEq {
/* ...existing overloads... */
bool operator()(const std::vector<int>& a, VectorHashMemo b) const {
return operator()(a, b.key);
}
bool operator()(VectorHashMemo a, const std::vector<int>& b) const {
return operator()(a.key, b);
}
bool operator()(VectorHashMemo b, VectorHashMemo a) const {
return operator()(a.key, b.key);
}
};
然后您可以只显式计算一次哈希,同时访问两次映射:
struct int_map_t : absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq> {
using absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq>::flat_hash_map;
using absl::flat_hash_map<std::vector<int>, int, VectorHash, VectorEq>::operator [];
int& operator [](absl::Span<int> v) {
VectorHashMemo hash = { absl::Hash<absl::Span<int>>{}(v), v };
auto it = find(hash);
if (it != end()) {
return it->second;
} else {
// calls the explicit conversion operator
return operator [](hash);
}
return operator [](std::vector<int> { v.begin(), v.end() });
}
};
演示:https ://godbolt.org/z/fecevE