gpt4 book ai didi

c++ - 当参数被转发时,std::move 在参数列表中是否安全,而不是 move 构造?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:32:46 26 4
gpt4 key购买 nike

试图为 std::string_view and std::string in std::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;
}

我用 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 之后才被清空(通过 move 构造存储在 map 中的 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 的参数(“值”,虽然 map 似乎使用 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))));

触发一个Segmentation fault在运行时,尽管它们都没有发出任何类型的警告,并且两个构造看起来同样未排序(工作中的未排序隐式转换 insert ,段错误中未排序的显式转换 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 以来我们是安全的是“稍后”构建的。

最佳答案

是的,您对 try_emplace 的调用是绝对安全的。 std::move 实际上并没有 move 任何东西,它只是将传递的变量转换为一个 xvalue。无论参数的初始化顺序如何,都不会 move 任何内容,因为参数都是引用。引用直接绑定(bind)到对象,它们不调用任何构造函数。

如果你看你的第二个片段,你会看到 std::make_pair 也通过引用获取它的参数,所以在这种情况下,除了在构造函数主体中,也不会进行 move .

然而,您的第三个片段确实存在 UB 问题。差别很小,但是如果 make_pair 的参数是从左到右求值的,那么一个临时的 std::unique_ptr 对象将被初始化为to_insert。这意味着现在 to_insert 为空,因为 move 确实发生了,因为您正在显式构建一个实际执行 move 的对象。

关于c++ - 当参数被转发时,std::move 在参数列表中是否安全,而不是 move 构造?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51752143/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com