假设您有一个std::unordered_set<std::string>
。
您在容器中有一个要搜索的std::string_view
对象。问题是,您不想从您的std::string
创建一个std::string_view
,因为这样会破坏首先使用std::string_view
的目的。
但是,似乎std::string_view
应该可以用作键;应该有一种比较std::string_view
和std::string
的方法,因为它们基本上代表同一件事。但是,无论如何,STL中都没有。
这是一个僵局,我是否被迫为std::string_view
和std::string
对象编写自己的比较对象,以与std::unordered_set
一起使用?
edit:此问题特定于string_view对象。 “重复”问题无关紧要。不出所料,我收到了一个独特问题的独特答案。
答案 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_string
是char*
,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
中的条目。如果在构建地图时两次遇到相同的键,则使用mymap
或mymap[key] = std::move(to_insert);
会中断,因为将保留原始insert_or_assign
(引用原始string_view
的数据),而该值将被新的string
替换,从而使string
的指针无效。尽管string_view
不能替换值,但我相信使用它需要一种更像是带有insert
的三层衬套的设计,因为如果您尝试插入try_emplace
将会是无序的构造视图和std::pair
作为unique_ptr
构造的一部分。