Array of polymorphic objects Array of polymorphic objects arrays arrays

Array of polymorphic objects


In very simple terms:

  • unique_ptr is the owner of the object. It manages the lifetime of the owned object

  • reference_wrapper wraps a pointer to an object in memory. It does NOT manage the lifetime of the wrapped object

You should create an array of unique_ptr (or shared_ptr) to guarantee the release of the object when it's not needed anymore.


If you are sufficiently motiviated, you can write a poly_any<Base> type.

A poly_any<Base> is an any restricted to only storing objects that derive from Base, and provides a .base() method that returns a Base& to the underlying object.

A very incomplete sketch:

template<class Base>struct poly_any:private std::any{  using std::any::reset;  using std::any::has_value;  using std::any::type;  poly_any( poly_any const& ) = default;  poly_any& operator=( poly_any const& ) = default;  Base& base() { return get_base(*this); }  Base const& base() const { return const_cast<Base const&>(get_base(const_cast<poly_any&>(*this))); }  template< class ValueType,    std::enable_if_t< /* todo */, bool > =true  >  poly_any( ValueType&& value ); // todo  // TODO: sfinae on ValueType?  template< class ValueType, class... Args >  explicit poly_any( std::in_place_type_t<ValueType>, Args&&... args );  // todo  // TODO: sfinae on ValueType?  template< class ValueType, class U, class... Args >  explicit poly_any( std::in_place_type_t<ValueType>, std::initializer_list<U> il,          Args&&... args ); // todo  void swap( poly_any& other ) {    static_cast<std::any&>(*this).swap(other);    std::swap( get_base, other.get_base );  }  poly_any( poly_any&& o ); // todo  poly_any& operator=( poly_any&& o ); // todo  template<class ValueType, class...Ts>  std::decay_t<ValueType>& emplace( Ts&&... ); // todo  template<class ValueType, class U, class...Ts>  std::decay_t<ValueType>& emplace( std::initializer_list<U>, Ts&&... ); // todoprivate:  using to_base = Base&(*)(std::any&);  to_base get_base = 0;};

Then you just have to intercept every means of putting stuff into the poly_any<Base> and store a get_base function pointer:

template<class Base, class Derived>auto any_to_base = +[](std::any& in)->Base& {  return std::any_cast<Derived&>(in);};

Once you have done this, you can create a std::vector<poly_any<Base>> and it is a vector of value types that are polymorphically descended from Base.

Note that std::any usually uses the small buffer optimization to store small objects internally, and larger objects on the heap. But that is an implementation detail.


Basically, a reference_wrapper is a mutable reference: Like a reference, it must not be null; but like a pointer, you can assign to it during its lifetime to point to another object.

However, like both pointers and references, reference_wrapper does not manage the lifetime of the object. That's what we use vector<uniq_ptr<>> and vector<shared_ptr<>> for: To ensure that the referenced objects are properly disposed off.

From a performance perspective, vector<reference_wrapper<T>> should be just as fast and memory efficient as vector<T*>. But both of these pointers/references may become dangling as they are not managing object lifetime.