std :: unordered_set中的std :: string_view和std :: string

时间:2018-08-08 14:22:03

标签: c++ stl c++17 unordered-set string-view

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

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

但是,似乎std::string_view应该可以用作键;应该有一种比较std::string_viewstd::string的方法,因为它们基本上代表同一件事。但是,无论如何,STL中都没有。

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

edit:此问题特定于string_view对象。 “重复”问题无关紧要。不出所料,我收到了一个独特问题的独特答案。

1 个答案:

答案 0 :(得分:2)

我没有一个很好的解决方案,但是使用最少的自定义代码的可能解决方法(以增加内存使用为代价)是将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 ++上有点实践,并且对使用{{1}持谨慎态度},与此表达式中的unique_ptr相同;在move 7.2上似乎有效,并且我认为的事实是g++的关键参数是立即构造的,而构造该值的参数则被转发{{3 }},但我会承认我对C ++评估顺序(或缺乏评估顺序)的理解并不完美。如果我在做非法的事情,而不仅仅是丑陋的话,那么修复它将需要稍微丑陋(但肯定是有序的):

try_emplace

附加说明:在此设计中,可以安全地使用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中的条目。如果在构建地图时两次遇到相同的键,则使用mymapmymap[key] = std::move(to_insert);会中断,因为将保留原始insert_or_assign(引用原始string_view的数据),而该值将被新的string替换,从而使string的指针无效。尽管string_view不能替换值,但我相信使用它需要一种更像是带有insert的三层衬套的设计,因为如果您尝试插入try_emplace将会是无序的构造视图和std::pair作为unique_ptr构造的一部分。