Array of polymorphic objects

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
11
down vote

favorite












I commonly come across the need to create arrays or vectors of polymorphic objects. I'd usually prefer to use references, rather than smart pointers, to the base class because they tend to be simpler.



Arrays and vectors are forbidden from containing raw references, and so I've tended to use smart pointers to the base classes instead. However, there is also the option to use std::reference_wrapper instead: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper



From what I can tell from the documentation, this is what one of its intended uses is, but when the topic of arrays containing polymorphic objects comes up, the common advice seems to be to use smart pointers rather than std::reference_wrapper.



My only thought is that smart pointers may be able to handle the lifetime of the object a little neater?



TL:DR; Why are smart pointers, such as std::unique_ptr seemingly preferred over std::reference_wrapper when creating arrays of polymorphic objects?







share|improve this question
















  • 8




    If the objects are owned somewhere else, reference_wrapper is fine. However, if this container owns the objects and controls their lifetime, you must use a smart pointer such as unique_ptr - reference_wrapper does not provide any ownership semantics.
    – BoBTFish
    Sep 7 at 12:34






  • 4




    unique_ptr manages the lifetime of the owned object, reference_wrapper stores a pointer to the object. Use unique_ptrs to guarantee the release of the object!
    – Alberto Miola
    Sep 7 at 12:34







  • 4




    a reference is just a reference, someone still needs to take care of the lifetime of the objects. With a smartpointer you get all you need at once
    – user463035818
    Sep 7 at 12:34






  • 2




    actually you already have the answer in your question. The only thing to add is that it is not about "neater", but references dont handle the lifetime at all
    – user463035818
    Sep 7 at 12:35






  • 1




    std::reference_wrapper as a container element type doesn't offer any advantages over a regular dumb pointer, except that the reference wrapper cannot be null.
    – n.m.
    Sep 7 at 12:50














up vote
11
down vote

favorite












I commonly come across the need to create arrays or vectors of polymorphic objects. I'd usually prefer to use references, rather than smart pointers, to the base class because they tend to be simpler.



Arrays and vectors are forbidden from containing raw references, and so I've tended to use smart pointers to the base classes instead. However, there is also the option to use std::reference_wrapper instead: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper



From what I can tell from the documentation, this is what one of its intended uses is, but when the topic of arrays containing polymorphic objects comes up, the common advice seems to be to use smart pointers rather than std::reference_wrapper.



My only thought is that smart pointers may be able to handle the lifetime of the object a little neater?



TL:DR; Why are smart pointers, such as std::unique_ptr seemingly preferred over std::reference_wrapper when creating arrays of polymorphic objects?







share|improve this question
















  • 8




    If the objects are owned somewhere else, reference_wrapper is fine. However, if this container owns the objects and controls their lifetime, you must use a smart pointer such as unique_ptr - reference_wrapper does not provide any ownership semantics.
    – BoBTFish
    Sep 7 at 12:34






  • 4




    unique_ptr manages the lifetime of the owned object, reference_wrapper stores a pointer to the object. Use unique_ptrs to guarantee the release of the object!
    – Alberto Miola
    Sep 7 at 12:34







  • 4




    a reference is just a reference, someone still needs to take care of the lifetime of the objects. With a smartpointer you get all you need at once
    – user463035818
    Sep 7 at 12:34






  • 2




    actually you already have the answer in your question. The only thing to add is that it is not about "neater", but references dont handle the lifetime at all
    – user463035818
    Sep 7 at 12:35






  • 1




    std::reference_wrapper as a container element type doesn't offer any advantages over a regular dumb pointer, except that the reference wrapper cannot be null.
    – n.m.
    Sep 7 at 12:50












up vote
11
down vote

favorite









up vote
11
down vote

favorite











I commonly come across the need to create arrays or vectors of polymorphic objects. I'd usually prefer to use references, rather than smart pointers, to the base class because they tend to be simpler.



Arrays and vectors are forbidden from containing raw references, and so I've tended to use smart pointers to the base classes instead. However, there is also the option to use std::reference_wrapper instead: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper



From what I can tell from the documentation, this is what one of its intended uses is, but when the topic of arrays containing polymorphic objects comes up, the common advice seems to be to use smart pointers rather than std::reference_wrapper.



My only thought is that smart pointers may be able to handle the lifetime of the object a little neater?



TL:DR; Why are smart pointers, such as std::unique_ptr seemingly preferred over std::reference_wrapper when creating arrays of polymorphic objects?







share|improve this question












I commonly come across the need to create arrays or vectors of polymorphic objects. I'd usually prefer to use references, rather than smart pointers, to the base class because they tend to be simpler.



Arrays and vectors are forbidden from containing raw references, and so I've tended to use smart pointers to the base classes instead. However, there is also the option to use std::reference_wrapper instead: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper



From what I can tell from the documentation, this is what one of its intended uses is, but when the topic of arrays containing polymorphic objects comes up, the common advice seems to be to use smart pointers rather than std::reference_wrapper.



My only thought is that smart pointers may be able to handle the lifetime of the object a little neater?



TL:DR; Why are smart pointers, such as std::unique_ptr seemingly preferred over std::reference_wrapper when creating arrays of polymorphic objects?









share|improve this question











share|improve this question




share|improve this question










asked Sep 7 at 12:32









user7119460

38728




38728







  • 8




    If the objects are owned somewhere else, reference_wrapper is fine. However, if this container owns the objects and controls their lifetime, you must use a smart pointer such as unique_ptr - reference_wrapper does not provide any ownership semantics.
    – BoBTFish
    Sep 7 at 12:34






  • 4




    unique_ptr manages the lifetime of the owned object, reference_wrapper stores a pointer to the object. Use unique_ptrs to guarantee the release of the object!
    – Alberto Miola
    Sep 7 at 12:34







  • 4




    a reference is just a reference, someone still needs to take care of the lifetime of the objects. With a smartpointer you get all you need at once
    – user463035818
    Sep 7 at 12:34






  • 2




    actually you already have the answer in your question. The only thing to add is that it is not about "neater", but references dont handle the lifetime at all
    – user463035818
    Sep 7 at 12:35






  • 1




    std::reference_wrapper as a container element type doesn't offer any advantages over a regular dumb pointer, except that the reference wrapper cannot be null.
    – n.m.
    Sep 7 at 12:50












  • 8




    If the objects are owned somewhere else, reference_wrapper is fine. However, if this container owns the objects and controls their lifetime, you must use a smart pointer such as unique_ptr - reference_wrapper does not provide any ownership semantics.
    – BoBTFish
    Sep 7 at 12:34






  • 4




    unique_ptr manages the lifetime of the owned object, reference_wrapper stores a pointer to the object. Use unique_ptrs to guarantee the release of the object!
    – Alberto Miola
    Sep 7 at 12:34







  • 4




    a reference is just a reference, someone still needs to take care of the lifetime of the objects. With a smartpointer you get all you need at once
    – user463035818
    Sep 7 at 12:34






  • 2




    actually you already have the answer in your question. The only thing to add is that it is not about "neater", but references dont handle the lifetime at all
    – user463035818
    Sep 7 at 12:35






  • 1




    std::reference_wrapper as a container element type doesn't offer any advantages over a regular dumb pointer, except that the reference wrapper cannot be null.
    – n.m.
    Sep 7 at 12:50







8




8




If the objects are owned somewhere else, reference_wrapper is fine. However, if this container owns the objects and controls their lifetime, you must use a smart pointer such as unique_ptr - reference_wrapper does not provide any ownership semantics.
– BoBTFish
Sep 7 at 12:34




If the objects are owned somewhere else, reference_wrapper is fine. However, if this container owns the objects and controls their lifetime, you must use a smart pointer such as unique_ptr - reference_wrapper does not provide any ownership semantics.
– BoBTFish
Sep 7 at 12:34




4




4




unique_ptr manages the lifetime of the owned object, reference_wrapper stores a pointer to the object. Use unique_ptrs to guarantee the release of the object!
– Alberto Miola
Sep 7 at 12:34





unique_ptr manages the lifetime of the owned object, reference_wrapper stores a pointer to the object. Use unique_ptrs to guarantee the release of the object!
– Alberto Miola
Sep 7 at 12:34





4




4




a reference is just a reference, someone still needs to take care of the lifetime of the objects. With a smartpointer you get all you need at once
– user463035818
Sep 7 at 12:34




a reference is just a reference, someone still needs to take care of the lifetime of the objects. With a smartpointer you get all you need at once
– user463035818
Sep 7 at 12:34




2




2




actually you already have the answer in your question. The only thing to add is that it is not about "neater", but references dont handle the lifetime at all
– user463035818
Sep 7 at 12:35




actually you already have the answer in your question. The only thing to add is that it is not about "neater", but references dont handle the lifetime at all
– user463035818
Sep 7 at 12:35




1




1




std::reference_wrapper as a container element type doesn't offer any advantages over a regular dumb pointer, except that the reference wrapper cannot be null.
– n.m.
Sep 7 at 12:50




std::reference_wrapper as a container element type doesn't offer any advantages over a regular dumb pointer, except that the reference wrapper cannot be null.
– n.m.
Sep 7 at 12:50












4 Answers
4






active

oldest

votes

















up vote
13
down vote



accepted










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.






share|improve this answer


















  • 1




    Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
    – Alberto Miola
    Sep 7 at 12:55


















up vote
3
down vote













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&&... ); // todo
private:
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.






share|improve this answer





























    up vote
    1
    down vote













    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.






    share|improve this answer



























      up vote
      1
      down vote













      Let's try the experiment:



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

      class Base
      public:
      Base()
      std::cout << "Base::Base()" << std::endl;


      virtual ~Base()
      std::cout << "Base::~Base()" << std::endl;

      ;

      class Derived: public Base
      public:
      Derived()
      std::cout << "Derived::Derived()" << std::endl;


      virtual ~Derived()
      std::cout << "Derived::~Derived()" << std::endl;

      ;

      typedef std::vector<std::reference_wrapper<Base> > vector_ref;
      typedef std::vector<std::shared_ptr<Base> > vector_shared;
      typedef std::vector<std::unique_ptr<Base> > vector_unique;

      void fill_ref(vector_ref &v)
      Derived d;
      v.push_back(d);


      void fill_shared(vector_shared &v)
      std::shared_ptr<Derived> d=std::make_shared<Derived>();
      v.push_back(d);


      void fill_unique(vector_unique &v)
      std::unique_ptr<Derived> d(new Derived());
      v.push_back(std::move(d));


      int main(int argc,char **argv)

      for(int i=1;i<argc;i++)
      if(std::string(argv[i])=="ref")
      std::cout << "vector" << std::endl;
      vector_ref v;
      fill_ref(v);
      std::cout << "~vector" << std::endl;
      else if (std::string(argv[i])=="shared")
      std::cout << "vector" << std::endl;
      vector_shared v;
      fill_shared(v);
      std::cout << "~vector" << std::endl;
      else if (std::string(argv[i])=="unique")
      std::cout << "vector" << std::endl;
      vector_unique v;
      fill_unique(v);
      std::cout << "~vector" << std::endl;





      running with argument shared:



      vector
      Base::Base()
      Derived::Derived()
      ~vector
      Derived::~Derived()
      Base::~Base()


      running with argument unique



      vector
      Base::Base()
      Derived::Derived()
      ~vector
      Derived::~Derived()
      Base::~Base()


      running with argument ref



      vector
      Base::Base()
      Derived::Derived()
      Derived::~Derived()
      Base::~Base()
      ~vector


      Explanation:




      • shared: Memory is shared by different parts of the code. In the example, the Derived object is first owned by the d local var in the function fill_shared() and by the vector. When the code exits the scope of the function object is still owned by the vector and only when the vector goes finally away, the object is deleted


      • unique: Memory is owned by the unique_ptr. In the example, the Derived object is first owned by the d local var. However it must be moved into the vector, transferring the ownership. Same as before, when the only owner goes away, the object gets deleted.


      • ref: There's no owning semantics. The object is created as a local variable of the fill_ref() function, and the reference to the object can be added to the vector. However, the vector does not own the memory and when the code goes out of the fill_ref() function, the object goes away, leaving the vector pointing to unallocated memory.





      share|improve this answer




















        Your Answer





        StackExchange.ifUsing("editor", function ()
        StackExchange.using("externalEditor", function ()
        StackExchange.using("snippets", function ()
        StackExchange.snippets.init();
        );
        );
        , "code-snippets");

        StackExchange.ready(function()
        var channelOptions =
        tags: "".split(" "),
        id: "1"
        ;
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function()
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled)
        StackExchange.using("snippets", function()
        createEditor();
        );

        else
        createEditor();

        );

        function createEditor()
        StackExchange.prepareEditor(
        heartbeatType: 'answer',
        convertImagesToLinks: true,
        noModals: false,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        );



        );













         

        draft saved


        draft discarded


















        StackExchange.ready(
        function ()
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52222665%2farray-of-polymorphic-objects%23new-answer', 'question_page');

        );

        Post as a guest






























        4 Answers
        4






        active

        oldest

        votes








        4 Answers
        4






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes








        up vote
        13
        down vote



        accepted










        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.






        share|improve this answer


















        • 1




          Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
          – Alberto Miola
          Sep 7 at 12:55















        up vote
        13
        down vote



        accepted










        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.






        share|improve this answer


















        • 1




          Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
          – Alberto Miola
          Sep 7 at 12:55













        up vote
        13
        down vote



        accepted







        up vote
        13
        down vote



        accepted






        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.






        share|improve this answer














        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.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Sep 7 at 12:56

























        answered Sep 7 at 12:40









        Alberto Miola

        2,29942139




        2,29942139







        • 1




          Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
          – Alberto Miola
          Sep 7 at 12:55













        • 1




          Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
          – Alberto Miola
          Sep 7 at 12:55








        1




        1




        Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
        – Alberto Miola
        Sep 7 at 12:55





        Try to always use smart pointers when you deal with objects that live in the heap. I use pointers like you do when I need to deal with polymorphism but I always try to not use pointers at all!
        – Alberto Miola
        Sep 7 at 12:55













        up vote
        3
        down vote













        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&&... ); // todo
        private:
        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.






        share|improve this answer


























          up vote
          3
          down vote













          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&&... ); // todo
          private:
          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.






          share|improve this answer
























            up vote
            3
            down vote










            up vote
            3
            down vote









            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&&... ); // todo
            private:
            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.






            share|improve this answer














            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&&... ); // todo
            private:
            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.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Sep 7 at 18:10

























            answered Sep 7 at 14:53









            Yakk - Adam Nevraumont

            169k18173349




            169k18173349




















                up vote
                1
                down vote













                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.






                share|improve this answer
























                  up vote
                  1
                  down vote













                  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.






                  share|improve this answer






















                    up vote
                    1
                    down vote










                    up vote
                    1
                    down vote









                    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.






                    share|improve this answer












                    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.







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Sep 7 at 12:51









                    cmaster

                    25k63578




                    25k63578




















                        up vote
                        1
                        down vote













                        Let's try the experiment:



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

                        class Base
                        public:
                        Base()
                        std::cout << "Base::Base()" << std::endl;


                        virtual ~Base()
                        std::cout << "Base::~Base()" << std::endl;

                        ;

                        class Derived: public Base
                        public:
                        Derived()
                        std::cout << "Derived::Derived()" << std::endl;


                        virtual ~Derived()
                        std::cout << "Derived::~Derived()" << std::endl;

                        ;

                        typedef std::vector<std::reference_wrapper<Base> > vector_ref;
                        typedef std::vector<std::shared_ptr<Base> > vector_shared;
                        typedef std::vector<std::unique_ptr<Base> > vector_unique;

                        void fill_ref(vector_ref &v)
                        Derived d;
                        v.push_back(d);


                        void fill_shared(vector_shared &v)
                        std::shared_ptr<Derived> d=std::make_shared<Derived>();
                        v.push_back(d);


                        void fill_unique(vector_unique &v)
                        std::unique_ptr<Derived> d(new Derived());
                        v.push_back(std::move(d));


                        int main(int argc,char **argv)

                        for(int i=1;i<argc;i++)
                        if(std::string(argv[i])=="ref")
                        std::cout << "vector" << std::endl;
                        vector_ref v;
                        fill_ref(v);
                        std::cout << "~vector" << std::endl;
                        else if (std::string(argv[i])=="shared")
                        std::cout << "vector" << std::endl;
                        vector_shared v;
                        fill_shared(v);
                        std::cout << "~vector" << std::endl;
                        else if (std::string(argv[i])=="unique")
                        std::cout << "vector" << std::endl;
                        vector_unique v;
                        fill_unique(v);
                        std::cout << "~vector" << std::endl;





                        running with argument shared:



                        vector
                        Base::Base()
                        Derived::Derived()
                        ~vector
                        Derived::~Derived()
                        Base::~Base()


                        running with argument unique



                        vector
                        Base::Base()
                        Derived::Derived()
                        ~vector
                        Derived::~Derived()
                        Base::~Base()


                        running with argument ref



                        vector
                        Base::Base()
                        Derived::Derived()
                        Derived::~Derived()
                        Base::~Base()
                        ~vector


                        Explanation:




                        • shared: Memory is shared by different parts of the code. In the example, the Derived object is first owned by the d local var in the function fill_shared() and by the vector. When the code exits the scope of the function object is still owned by the vector and only when the vector goes finally away, the object is deleted


                        • unique: Memory is owned by the unique_ptr. In the example, the Derived object is first owned by the d local var. However it must be moved into the vector, transferring the ownership. Same as before, when the only owner goes away, the object gets deleted.


                        • ref: There's no owning semantics. The object is created as a local variable of the fill_ref() function, and the reference to the object can be added to the vector. However, the vector does not own the memory and when the code goes out of the fill_ref() function, the object goes away, leaving the vector pointing to unallocated memory.





                        share|improve this answer
























                          up vote
                          1
                          down vote













                          Let's try the experiment:



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

                          class Base
                          public:
                          Base()
                          std::cout << "Base::Base()" << std::endl;


                          virtual ~Base()
                          std::cout << "Base::~Base()" << std::endl;

                          ;

                          class Derived: public Base
                          public:
                          Derived()
                          std::cout << "Derived::Derived()" << std::endl;


                          virtual ~Derived()
                          std::cout << "Derived::~Derived()" << std::endl;

                          ;

                          typedef std::vector<std::reference_wrapper<Base> > vector_ref;
                          typedef std::vector<std::shared_ptr<Base> > vector_shared;
                          typedef std::vector<std::unique_ptr<Base> > vector_unique;

                          void fill_ref(vector_ref &v)
                          Derived d;
                          v.push_back(d);


                          void fill_shared(vector_shared &v)
                          std::shared_ptr<Derived> d=std::make_shared<Derived>();
                          v.push_back(d);


                          void fill_unique(vector_unique &v)
                          std::unique_ptr<Derived> d(new Derived());
                          v.push_back(std::move(d));


                          int main(int argc,char **argv)

                          for(int i=1;i<argc;i++)
                          if(std::string(argv[i])=="ref")
                          std::cout << "vector" << std::endl;
                          vector_ref v;
                          fill_ref(v);
                          std::cout << "~vector" << std::endl;
                          else if (std::string(argv[i])=="shared")
                          std::cout << "vector" << std::endl;
                          vector_shared v;
                          fill_shared(v);
                          std::cout << "~vector" << std::endl;
                          else if (std::string(argv[i])=="unique")
                          std::cout << "vector" << std::endl;
                          vector_unique v;
                          fill_unique(v);
                          std::cout << "~vector" << std::endl;





                          running with argument shared:



                          vector
                          Base::Base()
                          Derived::Derived()
                          ~vector
                          Derived::~Derived()
                          Base::~Base()


                          running with argument unique



                          vector
                          Base::Base()
                          Derived::Derived()
                          ~vector
                          Derived::~Derived()
                          Base::~Base()


                          running with argument ref



                          vector
                          Base::Base()
                          Derived::Derived()
                          Derived::~Derived()
                          Base::~Base()
                          ~vector


                          Explanation:




                          • shared: Memory is shared by different parts of the code. In the example, the Derived object is first owned by the d local var in the function fill_shared() and by the vector. When the code exits the scope of the function object is still owned by the vector and only when the vector goes finally away, the object is deleted


                          • unique: Memory is owned by the unique_ptr. In the example, the Derived object is first owned by the d local var. However it must be moved into the vector, transferring the ownership. Same as before, when the only owner goes away, the object gets deleted.


                          • ref: There's no owning semantics. The object is created as a local variable of the fill_ref() function, and the reference to the object can be added to the vector. However, the vector does not own the memory and when the code goes out of the fill_ref() function, the object goes away, leaving the vector pointing to unallocated memory.





                          share|improve this answer






















                            up vote
                            1
                            down vote










                            up vote
                            1
                            down vote









                            Let's try the experiment:



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

                            class Base
                            public:
                            Base()
                            std::cout << "Base::Base()" << std::endl;


                            virtual ~Base()
                            std::cout << "Base::~Base()" << std::endl;

                            ;

                            class Derived: public Base
                            public:
                            Derived()
                            std::cout << "Derived::Derived()" << std::endl;


                            virtual ~Derived()
                            std::cout << "Derived::~Derived()" << std::endl;

                            ;

                            typedef std::vector<std::reference_wrapper<Base> > vector_ref;
                            typedef std::vector<std::shared_ptr<Base> > vector_shared;
                            typedef std::vector<std::unique_ptr<Base> > vector_unique;

                            void fill_ref(vector_ref &v)
                            Derived d;
                            v.push_back(d);


                            void fill_shared(vector_shared &v)
                            std::shared_ptr<Derived> d=std::make_shared<Derived>();
                            v.push_back(d);


                            void fill_unique(vector_unique &v)
                            std::unique_ptr<Derived> d(new Derived());
                            v.push_back(std::move(d));


                            int main(int argc,char **argv)

                            for(int i=1;i<argc;i++)
                            if(std::string(argv[i])=="ref")
                            std::cout << "vector" << std::endl;
                            vector_ref v;
                            fill_ref(v);
                            std::cout << "~vector" << std::endl;
                            else if (std::string(argv[i])=="shared")
                            std::cout << "vector" << std::endl;
                            vector_shared v;
                            fill_shared(v);
                            std::cout << "~vector" << std::endl;
                            else if (std::string(argv[i])=="unique")
                            std::cout << "vector" << std::endl;
                            vector_unique v;
                            fill_unique(v);
                            std::cout << "~vector" << std::endl;





                            running with argument shared:



                            vector
                            Base::Base()
                            Derived::Derived()
                            ~vector
                            Derived::~Derived()
                            Base::~Base()


                            running with argument unique



                            vector
                            Base::Base()
                            Derived::Derived()
                            ~vector
                            Derived::~Derived()
                            Base::~Base()


                            running with argument ref



                            vector
                            Base::Base()
                            Derived::Derived()
                            Derived::~Derived()
                            Base::~Base()
                            ~vector


                            Explanation:




                            • shared: Memory is shared by different parts of the code. In the example, the Derived object is first owned by the d local var in the function fill_shared() and by the vector. When the code exits the scope of the function object is still owned by the vector and only when the vector goes finally away, the object is deleted


                            • unique: Memory is owned by the unique_ptr. In the example, the Derived object is first owned by the d local var. However it must be moved into the vector, transferring the ownership. Same as before, when the only owner goes away, the object gets deleted.


                            • ref: There's no owning semantics. The object is created as a local variable of the fill_ref() function, and the reference to the object can be added to the vector. However, the vector does not own the memory and when the code goes out of the fill_ref() function, the object goes away, leaving the vector pointing to unallocated memory.





                            share|improve this answer












                            Let's try the experiment:



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

                            class Base
                            public:
                            Base()
                            std::cout << "Base::Base()" << std::endl;


                            virtual ~Base()
                            std::cout << "Base::~Base()" << std::endl;

                            ;

                            class Derived: public Base
                            public:
                            Derived()
                            std::cout << "Derived::Derived()" << std::endl;


                            virtual ~Derived()
                            std::cout << "Derived::~Derived()" << std::endl;

                            ;

                            typedef std::vector<std::reference_wrapper<Base> > vector_ref;
                            typedef std::vector<std::shared_ptr<Base> > vector_shared;
                            typedef std::vector<std::unique_ptr<Base> > vector_unique;

                            void fill_ref(vector_ref &v)
                            Derived d;
                            v.push_back(d);


                            void fill_shared(vector_shared &v)
                            std::shared_ptr<Derived> d=std::make_shared<Derived>();
                            v.push_back(d);


                            void fill_unique(vector_unique &v)
                            std::unique_ptr<Derived> d(new Derived());
                            v.push_back(std::move(d));


                            int main(int argc,char **argv)

                            for(int i=1;i<argc;i++)
                            if(std::string(argv[i])=="ref")
                            std::cout << "vector" << std::endl;
                            vector_ref v;
                            fill_ref(v);
                            std::cout << "~vector" << std::endl;
                            else if (std::string(argv[i])=="shared")
                            std::cout << "vector" << std::endl;
                            vector_shared v;
                            fill_shared(v);
                            std::cout << "~vector" << std::endl;
                            else if (std::string(argv[i])=="unique")
                            std::cout << "vector" << std::endl;
                            vector_unique v;
                            fill_unique(v);
                            std::cout << "~vector" << std::endl;





                            running with argument shared:



                            vector
                            Base::Base()
                            Derived::Derived()
                            ~vector
                            Derived::~Derived()
                            Base::~Base()


                            running with argument unique



                            vector
                            Base::Base()
                            Derived::Derived()
                            ~vector
                            Derived::~Derived()
                            Base::~Base()


                            running with argument ref



                            vector
                            Base::Base()
                            Derived::Derived()
                            Derived::~Derived()
                            Base::~Base()
                            ~vector


                            Explanation:




                            • shared: Memory is shared by different parts of the code. In the example, the Derived object is first owned by the d local var in the function fill_shared() and by the vector. When the code exits the scope of the function object is still owned by the vector and only when the vector goes finally away, the object is deleted


                            • unique: Memory is owned by the unique_ptr. In the example, the Derived object is first owned by the d local var. However it must be moved into the vector, transferring the ownership. Same as before, when the only owner goes away, the object gets deleted.


                            • ref: There's no owning semantics. The object is created as a local variable of the fill_ref() function, and the reference to the object can be added to the vector. However, the vector does not own the memory and when the code goes out of the fill_ref() function, the object goes away, leaving the vector pointing to unallocated memory.






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Sep 7 at 13:23









                            jsantander

                            4,171921




                            4,171921



























                                 

                                draft saved


                                draft discarded















































                                 


                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function ()
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52222665%2farray-of-polymorphic-objects%23new-answer', 'question_page');

                                );

                                Post as a guest













































































                                Comments

                                Popular posts from this blog

                                What does second last employer means? [closed]

                                List of Gilmore Girls characters

                                Confectionery