本周小贴士#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}) 也支持异构查找。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值