试图为std::unordered_set 中的 std::string_view 和 std::string提供解决方案,我正在玩替换std::unordered_set<std::string>
with std::unordered_map<std::string_view, std::unique_ptr<std::string>>
(该值是std::unique_ptr<std::string>
因为小字符串优化意味着string
' 的基础数据的地址将并不总是被转移的结果std::move
。
我的原始测试代码似乎有效,是(省略标题):
using namespace std::literals;
int main(int argc, char **argv) {
std::unordered_map<std::string_view, std::unique_ptr<std::string>> mymap;
for (int i = 1; i < argc; ++i) {
auto to_insert = std::make_unique<std::string>(argv[i]);
mymap.try_emplace(*to_insert, std::move(to_insert));
}
for (auto&& entry : mymap) {
std::cout << entry.first << ": " << entry.second << std::endl;
}
std::cout << std::boolalpha << "\"this\" in map? " << (mymap.count("this") == 1) << std::endl;
std::cout << std::boolalpha << "\"this\"s in map? " << (mymap.count("this"s) == 1) << std::endl;
std::cout << std::boolalpha << "\"this\"sv in map? " << (mymap.count("this"sv) == 1) << std::endl;
return EXIT_SUCCESS;
}
我用g++
7.2.0 编译,编译行g++ -O3 -std=c++17 -Wall -Wextra -Werror -flto -pedantic test_string_view.cpp -o test_string_view
没有收到任何警告,然后运行,得到以下输出:
$ test_string_view this is a test this is a second test
second: second
test: test
a: a
this: this
is: is
"this" in map? true
"this"s in map? true
"this"sv in map? true
这是我所期望的。
我主要关心的是:
mymap.try_emplace(*to_insert, std::move(to_insert));
已定义行为。*to_insert
依赖于在构造之后to_insert
才被清空(通过移动构造std::unique_ptr
存储在地图中)string_view
。将考虑的两个定义try_emplace
是:
try_emplace(const key_type& k, Args&&... args);
和
try_emplace(key_type&& k, Args&&... args);
我不确定会选择哪个,但无论哪种方式,它似乎key_type
都将作为调用的一部分构造try_emplace
,而用于制作mapped_type
(“值”) 的参数,尽管地图似乎用来value_type
指代组合的键/值pair
) 被转发,而不是立即使用,这使得代码被定义。我的解释是正确的,还是这种未定义的行为?
我担心的是,其他似乎绝对未定义的类似结构似乎仍然有效,例如:
mymap.insert(std::make_pair<std::string_view,
std::unique_ptr<std::string>>(*to_insert,
std::move(to_insert)));
产生预期的输出,而类似的结构如:
mymap.insert(std::make_pair(std::string_view(*to_insert),
std::unique_ptr<std::string>(std::move(to_insert))));
在运行时触发 a Segmentation fault
,尽管它们都没有发出任何类型的警告,并且两个构造似乎都同样无序(工作中的无序隐insert
式转换, segfaulting 中的无序显式转换insert
),所以我不想说“try_emplace
为我工作,所以没关系。”
请注意,虽然这个问题类似于C++11: std::move() call on arguments' list,但它并不完全是重复的(这可能是std::make_pair
这里不安全的原因,但不一定适用于try_emplace
基于转发的行为) ; 在那个问题中,接收参数的函数接收std::unique_ptr
,立即触发构造,同时try_emplace
接收转发的参数,而不是std::unique_ptr
,所以虽然std::move
“发生”(但还没有做任何事情),我认为我们是安全的,因为std::unique_ptr
“稍后”构造”。