Sha*_*ger 5 c++ move-semantics string-view c++17
试图提供解决方案的std ::性病string_view和的std :: string :: unordered_set,我与更换玩弄std::unordered_set<std::string>
与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;
}
Run Code Online (Sandbox Code Playgroud)
我用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
Run Code Online (Sandbox Code Playgroud)
这是我的预期.
我主要担心的是:
mymap.try_emplace(*to_insert, std::move(to_insert));
Run Code Online (Sandbox Code Playgroud)
定义了行为.的*to_insert
依赖于to_insert
不被排空(由移动构建std::unique_ptr
存储在映射)后直到string_view
被构造.try_emplace
将考虑的两个定义是:
try_emplace(const key_type& k, Args&&... args);
Run Code Online (Sandbox Code Playgroud)
和
try_emplace(key_type&& k, Args&&... args);
Run Code Online (Sandbox Code Playgroud)
我不确定会选择哪个,但无论哪种方式,它似乎key_type
都会被构造为调用的一部分try_emplace
,而制作mapped_type
("值"的参数,虽然map似乎value_type
用于指代组合的键/值pair
转发,而不是立即使用,这使代码定义.我在这种解释中是正确的,还是这种未定义的行为?
我担心的是,看起来绝对未定义的其他类似结构似乎仍然有效,例如:
mymap.insert(std::make_pair<std::string_view,
std::unique_ptr<std::string>>(*to_insert,
std::move(to_insert)));
Run Code Online (Sandbox Code Playgroud)
产生预期的输出,而类似的结构如:
mymap.insert(std::make_pair(std::string_view(*to_insert),
std::unique_ptr<std::string>(std::move(to_insert))));
Run Code Online (Sandbox Code Playgroud)
Segmentation fault
在运行时触发a ,尽管它们都没有引发任何类型的警告,并且这两个构造似乎同样没有序列(insert
在segfaulting中工作的,无序的显式转换中没有序列的隐式转换insert
),所以我不想说" try_emplace
为我工作,所以没关系."
请注意,虽然这个问题类似于C++ 11:std :: move()调用参数列表,但它并不是一个重复的(它可能会使std::make_pair
这里不安全,但不一定适用于try_emplace
基于转发的行为) ; 在那个问题中,接收参数的函数接收std::unique_ptr
,立即触发构造,同时try_emplace
接收转发参数,而不是std::unique_ptr
,所以当" std::move
已经发生"(但尚未完成)时,我认为我们是安全的,因为它std::unique_ptr
是"以后构建的" ".
是的,您拨打的电话try_emplace
是绝对安全的。std::move
实际上并没有移动任何东西,它只是将传递的变量转换为 xvalue。无论参数初始化的顺序如何,都不会移动任何内容,因为参数都是引用。引用直接绑定到对象,它们不调用任何构造函数。
如果您查看第二个片段,您会发现它std::make_pair
也通过引用获取其参数,因此在这种情况下,除了构造函数主体之外,也不会进行任何移动。
然而,你的第三个片段确实存在 UB 的问题。区别很微妙,但如果 的参数make_pair
从左到右求值,则临时std::unique_ptr
对象将使用 的移出值进行初始化to_insert
。这意味着现在to_insert
为 null,因为移动实际上发生了,因为您正在显式构造实际执行移动的对象。