PortMan
PortMan

Reputation: 4523

Can I use a `unique_ptr` in a vector, or do I need to switch to `shared_ptr`?

Given this class with a unique_ptr:

class MyClass
{
public:
  MyClass(){}
  MyClass(MyClass &&other) : ptr(std::move(other.ptr)){}

  std::unique_ptr <int> ptr;
};

Is there any way to make it possible to have a std::vector<MyClass>?

void ThisBreaksIt()
{
  MyClass instance;
  std::vector<MyClass> mv;
  mv.push_back(instance);
}

As-is, this gives me the error

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'

This makes sense, since I have no copy constrctor, and when the compiler tries creating a default copy constructor, it tries copying the unique_ptr, which isn't allowed.

I can make it compile by adding this constructor:

  MyClass(const MyClass&){}

But of course, this leaves the unique_ptr uninitialized, and is not what I want.

I can't add

  MyClass(const MyClass& other) : ptr(std::move(other.ptr)){}

because it's const, and I cant call std::move() on a const object. I can create the constructor

  MyClass(MyClass& other) : ptr(std::move(other.ptr)){}

but doesn't solve the original compile error, as vector::push_back uses a const copy constructor.

So, I'm stuck. Is there a way to do what I'm trying to do?

All of these issues go away if I just use a shared_ptr instead of unique_ptr. Is that what I should be doing?

Upvotes: 2

Views: 158

Answers (2)

Mike Seymour
Mike Seymour

Reputation: 254431

There's no problem storing a non-copyable type in a vector; it's only required to be movable, as yours is.

The problem is that this:

mv.push_back(instance);

tries to insert a copy of instance, and the class is not copyable. But it is movable:

mv.push_back(std::move(instance));

Note that there's no need to write your own default and move constructors in this example. The implicit ones will do the exactly what yours do.

Upvotes: 6

Werner Erasmus
Werner Erasmus

Reputation: 4076

You can use emplace_back. This only allows adding rvalues.

Therefore do this:

void ThisShouldFixIt()
{
  mv.emplace_back(MyClass());
}

Note that emplace_back can call the applicable constructor of T implace, as mentioned below. For what you are doing push_back is fine though, given that a version taking an rvalue reference does exist.

Upvotes: 0

Related Questions