10

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

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

但是,它似乎std::string_view应该可以用作键;应该有一些方法来比较std::string_viewand std::string,因为它们基本上都代表相同的东西。但无论如何,在 STL 中都没有。

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

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

4

1 回答 1

4

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

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

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

行不通。

相反,它必须是 a 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 定义了一个隐式operator std::string_view,并且std::string_view有一个隐式构造函数char*,所以成员资格测试仍然很简单:

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

是,还是. some_string_char*std::string_viewstd::string

注意:我不会发誓基于两行代码的插入代码是合法的,因为我在 C++ 上有点不习惯,并且对在我从中使用的同一表达式中try_emplace使用 a 持怀疑态度;在7.2 上它似乎有效,我认为关键参数是立即构造的,而构造值的参数被转发使得这很安全,但我承认我对 C++ 评估顺序的理解(或缺乏) 并不完美。如果我正在做一些非法的事情,而不仅仅是丑陋,那么修复它需要稍微丑陋一些(但肯定是有序的):unique_ptrmoveg++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_hinttry_emplacemymapmymap[key] = std::move(to_insert);insert_or_assignstring_viewstringstringstring_viewinserttry_emplacestd::pairunique_ptrpair

于 2018-08-08T16:03:00.640 回答