具有移动操作和重新值转发的对象生存期

Objects lifetime with move operation and rvalue forwarding

本文关键字:转发 对象 生存期 新值 移动 操作      更新时间:2023-10-16

我想知道以下代码是否有效
特别是,在调用new_S之后,我对这里涉及的对象的生存期感到困惑
根据我的理解,在处理初始值设定项列表时,T将被复制,并且可能在向量移动构造函数中被复制
那么RValue向量呢?从new_S返回后仍然有效吗?我会说不,但我绝对不确定

struct T
{
    int t;
};
struct S
{
    S(std::vector<T>&& s) : s_(std::forward<std::vector<T>>(s)) {}
    std::vector<T> s_;
};
S* new_S()
{
  return new S{{{1}, {2}, {3}}};
}

new_S返回后,没有"RValue vector"(我假设你指的是构造函数的右值引用参数(。右值参考仅存在于S.的构建过程中

对你的代码说几句话:std::forward在这里工作。它只需要完美地转发非全局引用,而std::vector<T>不是通用引用,而是右值引用(请参阅此处(。在这种情况下使用std::move

话虽如此,您不应该在构造函数中传递右值ref。相反,按值传递:

struct S
{
    S(std::vector<T> s) : s_{std::move(s)} {}
    std::vector<T> s_;
}; 

这样,您就可以获得任何传递值的最佳解决方案,请参阅此处:

  • 如果传递了一个右值,则s由参数移动构造,然后s_s移动构造。没有复制
  • 如果传递了一个左值,则s被复制构造,然后s_被从s移动构造。只制作了一份必要的副本

最后但同样重要的是:不要使用原始指针和new/delete。相反,使用智能指针:

unique_ptr<S> new_S()
{
  return std::make_unique<S>({{1}, {2}, {3}});
}

make_unique附带了C++14,如果您的库还没有,您可以轻松地推出自己的。

更新:要回答您关于对象生存期的问题:在new_S中,您基本上有4或5个对象:

  • 构造的S及其内部的向量
  • 传递给构造函数的初始值设定项列表
  • 根据初始值设定项列表构造的临时向量
  • 在我编写函数时,参数s是它自己的对象

现在发生了什么:

  1. 初始值设定项列表用于创建临时向量对象,它是传递给构造函数的右值。在构造临时的过程中,分配一块内存来将三个T放置在向量内
  2. 在编写过程中,构造函数的rvalue-ref参数绑定到临时的。在我的写作中,s是用临时的、调用向量的move构造函数初始化的。之后,临时内存为空,s拥有具有T的内存块
  3. 在初始化s_时,会移动参数。也就是说,s_是从临时(在你的写作中(或s(在我的写作中中(构造的。之后,临时/s为空,并且s_拥有在步骤1中分配的存储器块

s_构造正确,是一个有效的对象。对象X仅在移动时(即,当您调用move(X)时(变为"无效"。通过将另一个对象X移动到它来构造的对象Y(意思是:通过auto Y = move(X);移动构造它(永远不会无效。在构造中使对象无效将是非常糟糕和无用的,C++将是一种破碎的语言。