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_view
和 std::string
,因为它们基本上代表同一件事。但是没有,至少在STL中没有。
这是一个僵局,我是否被迫为 std::string_view
和 std::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_string
是 char*
、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
构建的一部分。
假设您有一个 std::unordered_set<std::string>
。
您有一个 std::string_view
对象要在容器中搜索。问题是,您不想从您的 std::string_view
创建一个 std::string
,因为这种首先违背了使用 std::string_view
的目的。
不过,看来std::string_view
应该可以作为钥匙使用;应该有一些方法可以比较 std::string_view
和 std::string
,因为它们基本上代表同一件事。但是没有,至少在STL中没有。
这是一个僵局,我是否被迫为 std::string_view
和 std::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_string
是 char*
、std::string_view
还是 std::string
。
注意:我不会发誓基于两行 try_emplace
的插入代码是合法的,因为我对 C++ 有点不熟悉,并且对使用 unique_ptr
在我 move
的同一个表达式中;在 g++
7.2 上它似乎工作,我 认为 立即构造 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
函数可以安全地用于更新[=35 中的条目=] 在这个设计中。如果在构建地图时遇到两次相同的键,则使用 mymap[key] = std::move(to_insert);
或 insert_or_assign
会中断,因为原始 string_view
(引用原始 string
的数据)将被保留,而该值将被新的 string
替换,使 string_view
的指针无效。虽然 insert
不会替换值,但我相信使用它需要一个更像带有 try_emplace
的三行代码的设计,因为如果您尝试插入 std::pair
将是无序的构建视图和 unique_ptr
作为 pair
构建的一部分。