1

I am trying to create an Entity which can hold a pointer to its parent and a vector to its children.

The problem is when I try to emplace_back or push_back to the vector of children, I get

Error C2280 'Entity::Entity(const Entity &)': attempting to reference a deleted function

Due to the fact I have a unique_ptr in the Entity.

I thought that adding a move-constructor would solve this problem but it has not.

I have included a minimal verifiable runnable example here below.

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

struct Entity
{
   //////// data
   unique_ptr<Entity> mParent;
   std::vector<Entity> mChildren;

   //////// ctors
   // default
   Entity() = default;
   // move
   Entity(Entity && entity): mParent{std::move(entity.mParent)}{}

   //////// functions
   void add_child(Entity const && entity)
   {
       mChildren.emplace_back(entity); // COMMENT OUT THIS LINE FOR FUNCTIONAL CODE
       //Error  C2280 'Entity::Entity(const Entity &)': attempting to reference a deleted function in... include\xmemory0 881
   };
};

int main()
{
   Entity entity;
   entity.add_child(Entity());
   return 0;
}
PrimeOfKnights
  • 426
  • 5
  • 17

1 Answers1

2

Drop the const:

void add_child(Entity && entity)

, and use:

mChildren.push_back(std::move(entity));

Applying these 2 changes above made it compile for me.

Explanation: You want to call void push_back( T&& value ); (or similarly with emplace_back) in vector<T>, where T is Entity. The other overload is void push_back( const T& value );, which doesn't compile, because its implementation (method body) attempts to call the copy constructor of T, and Entity doesn't have a copy constructor. The implementation void push_back( T&& value ); calls the move constructor of T, and Entity has a move constructor, so it compiles.

To make sure that void push_back( T&& value ); is called, you want to pass an Entity&& to push_back. For that you need both changes above. Without either, entity can't be converted to Entity&&.

See also https://en.cppreference.com/w/cpp/container/vector/push_back.

pts
  • 80,836
  • 20
  • 110
  • 183
  • @user4581301: Removing the `const`, but not adding `std::move` didn't make it compile for me. – pts Mar 20 '19 at 20:23
  • 1
    Yeah, you need the `std::move`. An rvalue reference is itself an lvalue expression. – François Andrieux Mar 20 '19 at 20:26
  • Semi-brainfart on my part. it's the change from `emplace_back` to `push_back` I was interested in. – user4581301 Mar 20 '19 at 20:26
  • @user4581301 They are equivalent in this case. [`std::vector::push_back`](https://en.cppreference.com/w/cpp/container/vector/push_back) has a `T&&` overload and both will construct an instance in place using the move constructor. – François Andrieux Mar 20 '19 at 20:27
  • I know. I'd hacked the Asker's code to add in the `std:: move` already and forgotten I'd done it. All I needed to make it work at that point was to remove the `const`. When I looked back at the asker's code I spotted what I'd done. – user4581301 Mar 20 '19 at 20:30
  • Thanks guys. I used std::forward instead of std::move and it works fine. is there a reason to prefer move over forward in this case? – Wesley Richmond Mar 22 '19 at 01:11