0

I am attempting to create a std::vector<SystemBase> class member that stores multiple systems that all derive from SystemBase. However this class has a reference and thus has no sensible default constructor:

class SystemBase {
public:
  SystemBase() = delete;
  explicit SystemBase(entt::registry &Registry) : Registry{Registry} {}
  virtual ~SystemBase() = default;

  SystemBase(const SystemBase &) = delete;
  SystemBase(SystemBase &&) = delete;
  auto operator=(const SystemBase &) -> SystemBase & = delete;
  auto operator=(SystemBase &&) -> SystemBase & = delete;

  virtual auto update(const sf::Time DeltaTime) const -> void;

protected:
  entt::registry &Registry;
};

A system that inherits looks as follows:

class LocalToWorldSystem final : public SystemBase {
public:
  explicit LocalToWorldSystem(entt::registry &Registry) : SystemBase{Registry} {};
  auto update(const sf::Time DeltaTime) const -> void override;
};

When trying to initialize a vector of systems I get an error:

std::vector<SystemBase> Systems{LocalToWorldSystem{Registry}};
//                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Error: Call to deleted constructor of 'const SystemBase'.

Why does this happen? My current workaround is to use a std::vector<std::unique_ptr<SystemBase>> but I'd like to know why this issue pops up in the first place.

Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74
  • 2
    A `std::vector` cannot contain objects of any derived type, only type `SystemBase` itself. – aschepler Apr 09 '21 at 02:48
  • @JerryJeremiah No, `std::vector` does not use a default constructor when not needed, or for unused memory. It's probably the move constructor it's complaining about, not default constructor. – aschepler Apr 09 '21 at 02:49
  • @aschepler You are right, it is complaining about `SystemBase(SystemBase &&) = delete;`. Just verified it. – Christian Ivicevic Apr 09 '21 at 02:52
  • @aschepler For testing purposes I made the move- and copy-constructor defaulted and it seems to work, but isn't semantically what I want to to be. – Christian Ivicevic Apr 09 '21 at 02:54
  • Right, if the base class allows copying, that vector will just slice off the base part of whatever you try to give it. Using `std::vector>` is probably the right thing to do, not just a workaround. – aschepler Apr 09 '21 at 02:56
  • https://stackoverflow.com/questions/11889178/c-can-vectorbase-contain-objects-of-type-derived – aschepler Apr 09 '21 at 02:58
  • I see, thank you for the input. If you want you can either answer the question or I can close it as a duplicate. – Christian Ivicevic Apr 09 '21 at 03:01
  • @aschepler I guess I was thinking of passing a number to the vector constructor which does call the default constructor. – Jerry Jeremiah Apr 09 '21 at 03:01

2 Answers2

1

In C++, variables and objects of type X are actually objects of type X. They are never objects of a derived type.

Pointers to base can point at derived. Derived objects have a base class subobject in them. But a vector of foo contains actual foos.

In many C++ derived languages, variables of object type are actually pointers (often called references) to said objects. This often leads to primitive types having value semantics, while object types have reference or pointer semantics.

A vector of unique ptrs, meanwhile, can store pointers to base that point to derived.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

I'd like to know why this issue pops up in the first place.

If you're coming from Java and similar languages, you need to realize objects in Java are implicitly references or pointers. They have an indirection.

In C++ objects do not have that indirection, they are the objects themselves. And so a SystemBase only has storage for SystemBase. It knows nothing about the space needed for derived classes.

Thus you need the std::unique_ptr to have the same indirection as in Java. This comes with a runtime cost, like in Java.

Acorn
  • 24,970
  • 5
  • 40
  • 69