将std::vector< std::unique_ptr<int> >的所有权正确地转移给正在构建的类的方法

31

如何将一个 std::vector<unique_ptr<int> > 的所有权转移给正在构建的类?以下是我想要做的代码表示。我意识到它不正确(无法编译),无论我是按值传递还是引用传递向构造函数传递向量都违反了“唯一性”。我希望Foo成为向量的新所有者,并希望调用函数放弃所有权。我需要构造函数采用 std::unique_ptr<std::vector<std::unique_ptr<int> > >实现吗?

Foo.h

class Foo
{
public:
  Foo(vector<std::unique_ptr<int> > vecOfIntPtrsOwnedByCaller);

private:
  vector<std::unique_ptr<int> > _vecOfIntPtrsOwnedByFoo;
}

Foo.cpp

Foo::Foo(std::vector<std::unique_ptr< int> > vecOfIntPtrsOwnedByCaller)
{
    _vecOfIntPtrsOwnedByFoo = vecOfIntPtrsOwnedByCaller;
}

希望能得到任何帮助 - 我搜索了整个网络以寻找正确的方法来完成这件事。谢谢!


1
std::vector<std::unique_ptr<int>> 需要使用临时变量或者调用者自己使用 std::move 来移动它们的向量,因为它不能被简单地复制。如果您想使其更加明确,请考虑使用 std::vector<...>&&。之后,在成员初始化器中使用 std::move 进行移动。 - Xeo
1
你想让向量是唯一拥有的,还是其中包含的元素? - Lightness Races in Orbit
轻量级竞速在轨道上 - Both. - Jen
Xeo - 感谢您的回复。我现在正在阅读有关rvalue引用的内容。如果我理解正确,构造函数应该接受一个指向vector的rvalue引用,并执行_vecOfIntPtrsOwnedByFoo(std::move(vecOfIntPtrsRvalRefence))。我不是完全清楚如何使传递到构造函数中的向量成为临时/ rvalue。我试图传递给构造函数的向量是另一个类的全局变量。 - Jen
1个回答

31

std::unique_ptr<T>是一种不可复制但可移动的类型。在std:vector<T>中有一个仅限移动的类型会使得std::vector<T>也成为仅限移动的类型。要让编译器自动移动对象,需要使用移动构造或移动赋值的r-value。在你的构造函数中,对象vecOfIntPtrsOwnedByCaller是一个l-value,尽管它已经拥有了指向的int,虽然它的名字表明是由调用者拥有的:当调用者创建对象时,这些指针就被“偷走”了。要从l-value移动,需要使用std::move()(或等价物):

Foo::Foo(std::vector<std::unique_ptr<int>> vecOfIntPtrsOwnedByCaller)
{
    _vecOfIntPtrsOwnedByFoo = std::move(vecOfIntPtrsOwnedByCaller);
}

或者,更可取的是

Foo::Foo(std::vector<std::unique_ptr<int>> vecOfIntPtrsOwnedByCaller)
    : _vecOfIntPtrsOwnedByFoo(std::move(vecOfIntPtrsOwnedByCaller))
{
}

后一种方法避免了首先默认构造成员,然后再对其进行移动分配的步骤,而是直接移动构造成员。我猜,我也会将参数作为右值引用,但这并不是必要的。

请注意,您只能从可以绑定到右值的内容构造Foo类型的对象,例如:

int main() {
    Foo f0(std::vector<std::unique_ptr<int>>()); // OK
    std::vector<std::unique_ptr<int>> v;
    Foo f1(v); v// ERROR: using with an l-value
    Foo f2{v}; v// ERROR: using with an l-value
    Foo f3 = v; // ERROR: using with an l-value
    Foo f4(std::move(v)); // OK: pretend that v is an r-value
}

参考和交换解决方案有效吗?我的意思是这个 Foo(vector<unique_ptr<int>>& v) { this->v.swap(v); }。坦率地说,我知道你的回答是正确的并且更好 - 但使用 swap 也可能是 OP “转移所有权”的请求的一种方式吗? - PiotrNycz
1
@PiotrNycz: 使用std::vector<...>swap()也可以,但在成员初始化列表中使用std::move()传输内容是惯用方式。 - Dietmar Kühl
谢谢Dietmar,您能示范一下使用rvalue引用的构造函数并展示如何将vector传递给它吗?您会如何决定使用哪个版本? - Jen
@Jen:在参数类型后面加上 && 就可以了。这样既不会改变实现,也不会改变使用方式。 - Dietmar Kühl
好的!非常感谢!我为什么要选择一种方式而不是另一种呢? - Jen
@Jen:传递值依赖于移动省略以避免为参数构造对象。当然,构造只是一个移动构造,但它会有成本。另一方面,移动省略不被应用的可能性很小,即我怀疑在实践中这并不重要。虽然我可能错过了什么。 - Dietmar Kühl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接