Array of polymorphic objects
Clash 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?
c++ arrays reference polymorphism smart-pointers
 |Â
show 1 more comment
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?
c++ arrays reference polymorphism smart-pointers
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 asunique_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
 |Â
show 1 more comment
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?
c++ arrays reference polymorphism smart-pointers
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?
c++ arrays reference polymorphism smart-pointers
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 asunique_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
 |Â
show 1 more comment
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 asunique_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
 |Â
show 1 more comment
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 objectreference_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.
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
add a comment |Â
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.
add a comment |Â
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.
add a comment |Â
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, theDerived
object is first owned by thed
local var in the functionfill_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, theDerived
object is first owned by thed
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 thefill_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 thefill_ref()
function, the object goes away, leaving the vector pointing to unallocated memory.
add a comment |Â
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 objectreference_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.
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
add a comment |Â
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 objectreference_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.
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
add a comment |Â
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 objectreference_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.
In very simple terms:
unique_ptr
is the owner of the object. It manages the lifetime of the owned objectreference_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.
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
add a comment |Â
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
add a comment |Â
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.
add a comment |Â
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.
add a comment |Â
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.
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.
edited Sep 7 at 18:10
answered Sep 7 at 14:53
Yakk - Adam Nevraumont
169k18173349
169k18173349
add a comment |Â
add a comment |Â
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.
add a comment |Â
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.
add a comment |Â
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.
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.
answered Sep 7 at 12:51
cmaster
25k63578
25k63578
add a comment |Â
add a comment |Â
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, theDerived
object is first owned by thed
local var in the functionfill_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, theDerived
object is first owned by thed
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 thefill_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 thefill_ref()
function, the object goes away, leaving the vector pointing to unallocated memory.
add a comment |Â
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, theDerived
object is first owned by thed
local var in the functionfill_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, theDerived
object is first owned by thed
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 thefill_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 thefill_ref()
function, the object goes away, leaving the vector pointing to unallocated memory.
add a comment |Â
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, theDerived
object is first owned by thed
local var in the functionfill_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, theDerived
object is first owned by thed
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 thefill_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 thefill_ref()
function, the object goes away, leaving the vector pointing to unallocated memory.
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, theDerived
object is first owned by thed
local var in the functionfill_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, theDerived
object is first owned by thed
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 thefill_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 thefill_ref()
function, the object goes away, leaving the vector pointing to unallocated memory.
answered Sep 7 at 13:23
jsantander
4,171921
4,171921
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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 asunique_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