将 std::forward_as_tuple() 结果传递给可能从该对象的 rvalue-reference 成员移动的多个函数?

Passing std::forward_as_tuple() result to multiple functions that may move from that object's rvalue-reference members?

本文关键字:rvalue-reference 对象 成员 函数 移动 tuple as forward std 结果      更新时间:2023-10-16

编辑:我想我所问的最有可能的用例是创建一个从std::forward_as_tuple()接收右值引用元组的函数

之所以想到这个问题,是因为我正在检查传递给构造函数初始化器的对象的成员,看看它们是否是右值引用(我愿意接受建议,告诉我这是错误的,错误的……希望遵循经验法则,在未来避免这种情况,但这就是问题的原因)。我突然想到,在一个稍微不同的上下文中,我可能会将一个具有右值引用成员的对象交给多个函数(或函数对象),我可能控制也可能不控制,这些函数可能会从这些成员中移动。

template<typename... Args>
void my_func(std::tuple<Args...>&& tup) {
    //if tup's members are rvalue references,
    //and this function moves guts from tup members, then...
    func_i_dont_control(tup); 
    //what happens here if moves are done on the same members?
    another_func_i_dont_control(std::move(tup));
}

我看过右值参考成员的使用?,以及其他一些关于右值参考成员的讨论,但我不能完全解决这个问题。

我不仅仅是问会发生什么,而是问这种情况是否应该/可能发生,以及在传递包含右值引用成员的对象时要记住什么关键规则。

在此代码中,func_i_dont_control无法静默地窃取参数。只有右值绑定到右值引用,并且命名变量不是右值。您的代码要么编译失败,要么func_i_dont_control(有一个重载)不使用移动语义。

为了让func_i_dont_control有机会窃取元组(将元组绑定到右值引用),必须使用std::move(tup)将其显式强制转换为右值。

(采用左值引用的函数不应该从中移动,否则我们真的无法判断会发生什么。)


编辑但问题似乎不是元组本身,而是它的成员。同样,这些成员不会是右值,所以func_i_dont_control需要显式地移动它们。我认为它没有这样做的"道德权利"*,除非它将整个元组作为右值接收,而这在你的函数中没有发生。

*对于移动语义,您必须遵循某些准则。基本上,你可以将任何东西强制转换为右值并从中移动,不管你是在处理左值还是右值引用。只要遵循这些准则,移动语义就会正常工作。如果您开始不顾这些准则将事物强制转换为右值,则对象将在函数调用等之后开始消失。

元组和可变模板的使用似乎偏离了问题的重点,所以让我重新表述一下:

void func(std::unique_ptr<Foo>&& foo) {
  std::unique_ptr<Foo> bar1(std::move(foo));
  std::unique_ptr<Foo> bar2(std::move(foo));
}

这里发生了什么?好吧,bar2总是包含一个空指针(而bar1可能会,这取决于foo的原始值)。

不存在未定义的行为,因为移动构造函数应该使对象处于可用状态需要引用,正是出于安全原因。因此,您可以使用从移动到的对象,但是它的状态可能不是很有趣:它甚至不需要等效于默认的构造状态,它可能只是一个过时的状态。