What is the proper way of transferring ownership of a std::vector<unique_ptr<int> >
to a class being constructed?
Below is a code representation of what I want to do. I realize it is not correct (won't compile) and violates "uniqueness" whether I pass the vector to the constructor by value or by reference. I want Foo to be the new owner of the vector, and want the calling function to relinquish ownership. Do I need the constructor to take a std::unique_ptr<std::vector<std::unique_ptr<int> > >
to do this?
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; }
Any help would be much appreciated - I've scoured the net looking for the correct way to do this. Thanks!
In C++11 we can transfer the ownership of an object to another unique_ptr using std::move() . After the ownership transfer, the smart pointer that ceded the ownership becomes null and get() returns nullptr.
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.
unique_ptr is not copyable, it is only moveable. This will directly affect Test, which is, in your second, example also only moveable and not copyable. In fact, it is good that you use unique_ptr which protects you from a big mistake.
This means that you can't make copies of a unique_ptr (because then two unique_ptr s would have ownership), so you can only move it. D.R. Since there can be only one, one should also be able to pass a temporary directly to the vector: vec. push_back(std::unique_ptr<int>(new int(1))); .
std::unique_ptr<T>
is a non-copyable but movable type. Having a move-only type in a std:vector<T>
make the std::vector<T>
move-only, too. To have the compiler automatically move objects, you need to have an r-value for move-construction or move-assignment. Within your constructor the object vecOfIntPtrsOwnedByCaller
is an l-value, although one which, despite its name, already owns the pointed to int
s: they got "stolen" from the caller when the caller created the object. To move from an l-value, you need to use std::move()
(or something equivalent):
Foo::Foo(std::vector<std::unique_ptr<int>> vecOfIntPtrsOwnedByCaller) { _vecOfIntPtrsOwnedByFoo = std::move(vecOfIntPtrsOwnedByCaller); }
or, preferable
Foo::Foo(std::vector<std::unique_ptr<int>> vecOfIntPtrsOwnedByCaller) : _vecOfIntPtrsOwnedByFoo(std::move(vecOfIntPtrsOwnedByCaller)) { }
The latter approach avoid first default-constructing the member and then move-assigning to it and, instead, move-constructs the member directly. I guess, I would also make the argument an r-value reference but this isn't necessary.
Note, that you can construct objects of type Foo
only from something which can be bound to an r-value, e.g.:
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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With