0

New to C++ and mostly enjoying the learning curve, but struggling to solve this one.

I'm needing to satisfy this requirement in two different related places

std::unique_ptr [is typically used] as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired)

Compilable code (toggle the "working" and "not working" to cause build/fail):

#include<vector>
#include<memory>

class Entity;

class Action
{
    public:
        Action() = default;
};

class World {
    std::vector<std::unique_ptr<Entity> > entities;
    public:
        World();
};

class Entity {
    public:
        Entity() = default;
};

class Animal: public Entity {
    protected:
        // Not working - log below
        std::vector<std::unique_ptr<Action> > actions;
        // Working fine but doesn't support polymorphic behaviour
        //std::vector<Action> actions;
    public:
        Animal() = default;
};

class Person: public Animal
{
    protected:
        World& world;
    public: 
        Person(World& world);
};

Person::Person(World& world) : world(world)
{
}

World::World()
{
    Person first = Person(*this);
    entities.push_back(std::make_unique<Person>(first));
}

Which barfs the error:

/usr/include/c++/9/bits/stl_uninitialized.h|307 col 37 error| required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<Action>*, std::vector<std::unique_ptr<Action> > >; _ForwardIterator = std::unique_ptr<Action>*; _Tp = std::unique_ptr<Action>]’
/usr/include/c++/9/bits/stl_vector.h|555 col 31 error| required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<Action>; _Alloc = std::allocator<std::unique_ptr<Action> >]’
test.cpp|23 col 7 error| required from ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = Person; _Args = {Person&}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<Person, std::default_delete<Person> >]’
test.cpp|48 col 54 error| required from here
/usr/include/c++/9/bits/stl_uninitialized.h|127 col 72 error| static assertion failed: result type must be constructible from value type of input range

So it seems that Person first is correctly created, but the error is coming from when make_unique<Person>(first) is handling the unique_ptr of first.actions. Maybe. I'm not following the process of why declaration is triggering the uninitialized_copy() merely through lack of understanding, and though there's reams of similar questions around unique_ptr, I find no direct references to the "nesting" thing I've got going on and 4 hours on this is about enough before resorting to asking here.

Not necessarily after a full solution, but some hints that will get me looking at the right direction would be much appreciated. Newb-level speak would be appreciated :-)

Thanks in anticipation.

Edit: Code snippet changed to be full and compilable using gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0

My bad - vi coughed up the same make_unique() error as before, but compiling throws other errors - will not fix as question has been fully answered

markdwhite
  • 2,360
  • 19
  • 24
  • 4
    Your code as is won't compile enough to let us reproduce the issue. Can we get a [mre] that generate the error you are getting? – NathanOliver Jan 23 '23 at 13:06
  • circular dependency is main problem here. – Marek R Jan 23 '23 at 13:45
  • @NathanOliver - full code now added – markdwhite Jan 23 '23 at 13:51
  • @MarekR - it compiles fine when with std::vector actions; rather than std::vector > actions; which suggests class definitions are available when they are needed, but I'm wanting to fill gaps in my knowledge so please explain more – markdwhite Jan 23 '23 at 13:57
  • 1
    @markdwhite "compiles fine" doesn't mean this is not a problem. Any kind of circular dependency in IT is a big problem. There are some exceptions (like nodes in a list), but your code is not one of them. – Marek R Jan 23 '23 at 17:26

1 Answers1

2

std::vector<std::unique_ptr<Action>> is not copyable because std::unique_ptr<Action> is not copyable.

std::make_unique<Person>(first) tries to copy first.

This can be fixed by moving (std::make_unique<Person>(std::move(first))), but you can also forgo the intermediate local entirely:

entities.push_back(std::make_unique<Person>(*this));
Artyer
  • 31,034
  • 3
  • 47
  • 75
  • And that's all it was... I'd understood that std::make_unique() avoided the need to use std::move(), and wrongly assumed that would avoid the copy when using a local var too. But now you point it out, it's clear where the copy is happening. Live and learn :-) Thx! – markdwhite Jan 23 '23 at 14:04