std::string_view 和 std::string 在 std::unordered_set

std::string_view and std::string in std::unordered_set

假设您有一个 std::unordered_set<std::string>

您有一个 std::string_view 对象要在容器中搜索。问题是,您不想从您的 std::string_view 创建一个 std::string,因为这种首先违背了使用 std::string_view 的目的。

不过,看来std::string_view应该可以作为钥匙使用;应该有一些方法可以比较 std::string_viewstd::string ,因为它们基本上代表同一件事。但是没有,至少在STL中没有。

这是一个僵局,我是否被迫为 std::string_viewstd::string 对象编写自己的比较对象以与我的 std::unordered_set 一起使用?

编辑:此问题特定于 string_view 个对象。 'duplicate' 问题不相关。正如预期的那样,我收到了一个独特问题的独特答案。

我没有很好的解决方案,但一个可能的解决方法是使用最少的自定义代码,以增加内存使用为代价,将您的 std::unordered_set<std::string> 替换为 std::unordered_map键的视图和值的字符串(支持视图)。

不幸的是,由于小字符串优化,我们不能依赖 std::move 保留底层 string 数据的原始地址,所以像:

std::string to_insert(...);
mymap.try_emplace(to_insert, std::move(to_insert));

行不通。

相反,它必须是 std::unordered_map<std::string_view, std::unique_ptr<std::string>> 这样我们就可以保留字符串字符的唯一地址,使代码更像:

auto to_insert = std::make_unique<std::string>(...);
mymap.try_emplace(*to_insert, std::move(to_insert));

虽然插入有点难看,但简单的成员资格测试仍然很简单,因为 std::string defines an implicit operator std::string_view,并且 std::string_view 有一个 char* 的隐式构造函数,所以成员资格测试仍然很简单:

if (mymap.count(some_string)) { ... }

some_stringchar*std::string_view 还是 std::string

注意:我不会发誓基于两行 try_emplace 的插入代码是合法的,因为我对 C++ 有点不熟悉,并且对使用 unique_ptr 在我 move 的同一个表达式中;在 g++ 7.2 上它似乎工作,我 认为 立即构造 try_emplace 的关键参数,而构造值的参数被转发,但我承认我对 C++ 求值顺序(或缺乏)的理解并不完美。如果我做的事情是非法的,而不仅仅是丑陋的,那么修复它需要稍微丑陋的(但绝对是有序的):

auto to_insert = std::make_unique<std::string>(...);
std::string_view key{*to_insert};
mymap.try_emplace(std::move(key), std::move(to_insert));

附加说明:只有 emplace/emplace_hint/try_emplace 函数可以安全地用于更新[=35 中的条目=] 在这个设计中。如果在构建地图时遇到两次相同的键,则使用 mymap[key] = std::move(to_insert);insert_or_assign 会中断,因为原始 string_view(引用原始 string 的数据)将被保留,而该值将被新的 string 替换,使 string_view 的指针无效。虽然 insert 不会替换值,但我相信使用它需要一个更像带有 try_emplace 的三行代码的设计,因为如果您尝试插入 std::pair 将是无序的构建视图和 unique_ptr 作为 pair 构建的一部分。