本周小贴士#144:关联容器中的异构查找
Posted -飞鹤-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了本周小贴士#144:关联容器中的异构查找相关的知识,希望对你有一定的参考价值。
作为TotW#144最初发表于2018年3月23日
由Samuel Benzaquen创作
介绍
关联容器使用一个关键字来关联一个元素。插入或从容器中查找元素需要等效键。 通常,容器要求键是特定类型的,这会导致调用处的效率低下,需要在接近等效的类型(如 std::string 和 absl::string_view)之间进行转换。
为了避免这种不必要的工作,一些容器提供了异构查找。 此功能允许调用者传递任何类型的键(只要用户指定的比较器函子支持它们)。 有关 STL 容器中此功能的示例,请参见 std::map::find。
透明函数子
透明函数子是一种接受不止一种特定类型的函数子。 它必须通过提供一个 is_transparent 内部类型来公开这个事实。 此内部类型的实际定义不相关,因为它仅用作标记。 通常使用将 is_transparent 设置为 void 的 using 声明。
当容器检测到透明函子时,它们的查找函数将原封不动地转发用户指定的值,而不是首先将其转换为 key_type(通过隐式或显式转换)。
隐式支持异构查找可能很危险,因为值之间的关系在转换后可能无法保持。 例如,1.0 < 1.1,但 static_cast(1.0) == static_cast(1.1)。 因此,使用 double 在 std::set 中查找值可能会导致不正确的结果。 这些潜在的错误是选择加入此功能的原因。
使用异构查找来提高性能
使用异构查找的一个常见原因是性能。 我们可以构造 key_type,但这样做需要我们宁愿避免的复杂的工作。 例如:
std::map<std::string, int> m = ...;
absl::string_view some_key = ...;
// 构造一个临时'std::string‘去执行查询
// 分配+拷贝+释放可能支配find()调用
auto it = m.find(std::string(some_key));
相反,我们可以像这样使用透明比较器:
struct StringCmp
using is_transparent = void;
bool operator()(absl::string_view a, absl::string_view b) const
return a < b;
;
std::map<std::string, int, StringCmp> m = ...;
absl::string_view some_key = ...;
// 比较器’StringCmp'将接受任意能够隐式转换为'absl::string_view'的类型,并且
// 通过声明‘is_transparent'标签来说明。
// 我们能够传递'some_key'而不用先转换它为'std::string'来进行'find()'。
// 在这种情况下,这避免了需要构造‘std::string’实例的不必要内存分配。
auto it = m.find(some_key);
还有什么好处?
存在不可能或不方便创建有效的 key_type 对象只是为了进行查找的情况。 在这些情况下,我们可能希望使用更容易生成但包含查找所需信息的替代类型。 例如:
struct ThreadCmp
using is_transparent = void;
// 规则重载
bool operator()(const std::thread& a, const std::thread& b) const
return a.get_id() < b.get_id();
// 透明重载
bool operator()(const std::thread& a, std::thread::id b) const
return a.get_id() < b;
bool operator()(std::thread::id a, const std::thread& b) const
return a < b.get_id();
bool operator()(std::thread::id a, std::thread::id b) const
return a < b;
;
std::set<std::thread, ThreadCmp> threads = ...;
// 不能使用相同id来构建一个'std;:thread"实例,仅用来进行查询。
// 但是我们能够通过id来查找。
std::thread::id id = ...;
auto it = threads.find(id);
STL 容器支持和替代方案
有序容器 (std::map,set,multimap,multiset) 从 C++14 开始支持异构查找。 从 C++17 开始,无序容器仍然不支持它。 有添加此功能的建议,但尚未被接受。
然而,Swiss表的新系列支持对类字符串类型(std::string、absl::string_view 等)和智能指针(T*、std::shared_ptr、std::unique_ptr)的异构查找。 它们要求哈希函数和相等函数都被标记为透明的。 所有其他密钥类型都需要用户明确选择加入。
B-Tree 容器 (absl::btree_set,map,multiset,multimap) 也支持异构查找。
以上是关于本周小贴士#144:关联容器中的异构查找的主要内容,如果未能解决你的问题,请参考以下文章