1

我想知道以下代码是否有效。
特别是,在调用 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}}};
}
4

1 回答 1

3

new_S那里返回后没有“RValue 向量”(我假设您的意思是构造函数的右值引用参数)。右值引用仅在 S 的构造期间存在。

对您的代码说几句话:std::forward在这里磨损。仅在完美转发非通用引用时才需要它,并且std::vector<T>uns 不是通用引用而是右值引用(请参见此处)std::move在这种情况下使用。

话虽如此,您不应在构造函数中通过 rvalue-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. 在您的写作中,构造函数的右值 ref 参数绑定到临时。在我的写作中,s使用临时调用向量的移动构造函数进行初始化。在那之后,临时是空的,并s拥有带有 T 的内存块。
  3. 在 的初始化中s_,参数被移动。这意味着,从临时(在您的写作中)或从(在我的写作中)s_构建移动。s之后,临时/s为空并s_拥有在步骤 1 中分配的内存块。

s_构造正确,它是一个有效的对象。一个对象X只有在移动它时才会“无效”,即当你调用move(X). Y您通过将另一个对象移动到它来构造的对象(X意思是:通过 移动构造它auto Y = move(X);)永远不会是无效的。在构造时使对象无效将是非常糟糕和无用的,C++ 将是一种损坏的语言。

于 2013-05-24T06:46:02.140 回答