31

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!

Xeo
  • 129,499
  • 52
  • 291
  • 397
Jen
  • 313
  • 1
  • 3
  • 6
  • 1
    `std::vector>` requires either a temporary, or the caller to use `std::move` on their own vector, since it can't just be copied in. If you want to make it really explicit, consider `std::vector<...>&&`. After that, move into your member (in the member-initializers!) with `std::move`. – Xeo Aug 16 '13 at 21:58
  • 1
    Do you want the vector to be uniquely-owned, or the elements contained therein? – Lightness Races in Orbit Aug 16 '13 at 22:05
  • Lightness Races in Orbit - Both. – Jen Aug 16 '13 at 22:23
  • Xeo - thanks for your response. I am just reading on rvalue references now. If I understand you correctly, the constructor should take an rvalue reference to a vector and do _vecOfIntPtrsOwnedByFoo(std::move(vecOfIntPtrsRvalRefence)). I am not entirely clear how to make the vector passed into the constructor a temporary/rvalue. The vector I am trying to pass to the constructor is a global variable of another class. – Jen Aug 16 '13 at 22:42

1 Answers1

31

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 ints: 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
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Is solution with reference and swap valid? I mean this `Foo(vector>& v) { this->v.swap(v); }`. Frankly I know your answer is correct and better - but using `swap` could be also a way to OP's request of "transferring ownership"? – PiotrNycz Aug 16 '13 at 22:51
  • 1
    @PiotrNycz: Using `std::vector<...>`'s `swap()` would work, too, but using `std::move()` in the member initializer list is the idiomatic way to transfer the content. – Dietmar Kühl Aug 16 '13 at 22:54
  • Thanks Dietmar, would you mind showing an example of a constructor taking a rvalue reference also and how to pass a vector to it? How do you decide which version to use? – Jen Aug 16 '13 at 22:57
  • @Jen: Just add a `&&` after the argument type. Neither the implementation nor the use would change. – Dietmar Kühl Aug 16 '13 at 23:00
  • Ok! Thank you so much! Why would I choose one way over the other? – Jen Aug 16 '13 at 23:08
  • @Jen: Passing by value relies on move-elision to be applied to avoid constructing an object for the argument. Sure, the construction is just a move-construction but it would have a cost. On the other hand, it is unlikely that move-elision is not applied, i.e., I doubt it matters in practice. I might be missing something, though. – Dietmar Kühl Aug 16 '13 at 23:11