2

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?

PortMan
  • 4,205
  • 9
  • 35
  • 61

2 Answers2

6

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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • You will get similar error symptoms if you write "vector> myVector = someOtherVector" instead of properly declaring myVector to be a reference. VS2010 especially will give you very misleading error messages. – Daniel Dec 06 '18 at 08:07
0

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.

Community
  • 1
  • 1
Werner Erasmus
  • 3,988
  • 17
  • 31
  • `emplace_back` doesn't really change anything here; `mv.push_back(MyClass())` would also be fine. `emplace_back` is for when you want to pass constructor parameters to create an object in place, rather than passing an existing object to move or copy. – Mike Seymour Sep 10 '14 at 16:09
  • Agreed. I've changed my answer as I consider it worthwhile being aware of the benefits of emplace. – Werner Erasmus Sep 10 '14 at 16:16