1. Change history
Changes in P0201R6
-
Further clarifications to formal wording.
-
Allocator support.
-
Remove note suggesting that a small object optimisation is allowed.
-
support.constexpr
Changes in P0201R5
-
Make constructor of
default_copy
andnoexcept
.constexpr -
Clarifications to formal wording.
Changes in P0201R4
-
Clarify authors' agreement with LEWG design changes.
-
Add wording to clarify meaning of custom copier and deleter.
-
Make constructors explicit and remove converting assignment.
-
Add a second template parameter to
to facilitate construction of objects of derived classes.make_polymorphic_value
Changes in P0201R3
-
Add rationale for absence of allocator support.
Changes in P0201R2
-
Change name to
.polymorphic_value -
Remove
.operator << -
Add construction and assignment from values.
-
Use
.std :: default_delete -
Rename
tostd :: default_copier
.std :: default_copy -
Add notes on empty state and pointer constructor.
-
Add
exception when static and dynamic type of pointee mismatch and no custom copier or deleter are supplied.bad_polymorphic_value_construction -
Add clarifying note to say that a small object optimisation is allowed.
Changes in P0201R1
-
Change name to
.indirect -
Remove
,static_cast
anddynamic_cast
asconst_cast
is modelled on a value not a pointer.polymorphic_value -
Add
accessors which returnconst
references/pointers.const -
Remove pointer-accessor
.get -
Remove specialization of
.propagate_const -
Amended authorship and acknowledgements.
-
Added support for custom copiers and custom deleters.
-
Removed hash and comparison operators.
2. TL;DR
Add a class template,
, to the standard library to support
polymorphic objects with value-like semantics.
3. Introduction
The class template,
, confers value-like semantics on a
free-store allocated object. A
may hold an object of a
class publicly derived from T, and copying the
will copy
the object of the derived type.
3.1. Motivation: Composite objects
Use of components in the design of object-oriented class hierarchies can aid modular design as components can be potentially re-used as building-blocks for other composite classes.
We can write a simple composite object formed from two components as follows:
// Simple composite class CompositeObject_1 { Component1 c1_ ; Component2 c2_ ; public : CompositeObject_1 ( const Component1 & c1 , const Component2 & c2 ) : c1_ ( c1 ), c2_ ( c2 ) {} void foo () { c1_ . foo (); } void bar () { c2_ . bar (); } };
The composite object can be made more flexible by storing pointers to objects allowing it to take derived components in its constructor. (We store pointers to the components rather than references so that we can take ownership of them).
// Non-copyable composite with polymorphic components (BAD) class CompositeObject_2 { IComponent1 * c1_ ; IComponent2 * c2_ ; public : CompositeObject_2 ( IComponent1 * c1 , IComponent2 * c2 ) : c1_ ( c1 ), c2_ ( c2 ) {} void foo () { c1_ -> foo (); } void bar () { c2_ -> bar (); } CompositeObject_2 ( const CompositeObject_2 & ) = delete ; CompositeObject_2 & operator = ( const CompositeObject_2 & ) = delete ; CompositeObject_2 ( CompositeObject_2 && o ) : c1_ ( o . c1_ ), c2_ ( o . c2_ ) { o . c1_ = nullptr ; o . c2_ = nullptr ; } CompositeObject_2 & operator = ( CompositeObject_2 && o ) { delete c1_ ; delete c2_ ; c1_ = o . c1_ ; c2_ = o . c2_ ; o . c1_ = nullptr ; o . c2_ = nullptr ; } ~ CompositeObject_2 () { delete c1_ ; delete c2_ ; } };
's constructor API is unclear without knowing that the class
takes ownership of the objects. We are forced to explicitly suppress the
compiler-generated copy constructor and copy assignment operator to avoid
double-deletion of the components
and
. We also need to write a move
constructor and move assignment operator.
Using
makes ownership clear and saves us writing or deleting
compiler generated functions:
// Non-copyable composite with polymorphic components class CompositeObject_3 { std :: unique_ptr < IComponent1 > c1_ ; std :: unique_ptr < IComponent2 > c2_ ; public : CompositeObject_3 ( std :: unique_ptr < IComponent1 > c1 , std :: unique_ptr < IComponent2 > c2 ) : c1_ ( std :: move ( c1 )), c2_ ( std :: move ( c2 )) {} void foo () { c1_ -> foo (); } void bar () { c2_ -> bar (); } };
The design of
is good unless we want to copy the object.
We can avoid having to define our own copy constructor by using shared
pointers. As
's copy constructor is shallow, we need to modify the
component pointers to be pointers-to
to avoid introducing shared mutable
state [S.Parent].
// Copyable composite with immutable polymorphic components class class CompositeObject_4 { std :: shared_ptr < const IComponent1 > c1_ ; std :: shared_ptr < const IComponent2 > c2_ ; public : CompositeObject_4 ( std :: shared_ptr < const IComponent1 > c1 , std :: shared_ptr < const IComponent2 > c2 ) : c1_ ( std :: move ( c1 )), c2_ ( std :: move ( c2 )) {} void foo () { c1_ -> foo (); } void bar () { c2_ -> bar (); } };
has polymorphism and compiler-generated destructor, copy,
move and assignment operators. As long as the components are not mutated, this
design is good. If non-const functions of components are used then this won’t
compile.
Using
a copyable composite object with polymorphic
components can be written as:
// Copyable composite with mutable polymorphic components class CompositeObject_5 { std :: polymorphic_value < IComponent1 > c1_ ; std :: polymorphic_value < IComponent2 > c2_ ; public : CompositeObject_5 ( std :: polymorphic_value < IComponent1 > c1 , std :: polymorphic_value < IComponent2 > c2 ) : c1_ ( std :: move ( c1 )), c2_ ( std :: move ( c2 )) {} void foo () { c1_ -> foo (); } void bar () { c2_ -> bar (); } };
The component
can be constructed from an instance of any class that
inherits from
. Similarly,
can be constructed from an
instance of any class that inherits from
.
has a compiler-generated destructor, copy constructor, move
constructor, assignment operator and move assignment operator. All of these
compiler-generated functions will behave correctly.
3.2. Deep copies
To allow correct copying of polymorphic objects,
uses the
copy constructor of the owned derived-type object when copying a base type
. Similarly, to allow correct destruction of polymorphic
component objects,
uses the destructor of the owned
derived-type object in the destructor of a base type
.
The requirements of deep-copying can be illustrated by some simple test code:
// GIVEN base and derived classes. class Base { virtual void foo () const = 0 ; }; class Derived : public Base { void foo () const override {} }; // WHEN a polymorphic_value to base is formed from a derived object polymorphic_value < Base > poly ( Derived ()); // AND the polymorphic_value to base is copied. auto poly_copy = poly ; // THEN the copy owns a distinct object assert ( &* poly != &* poly_copy ); // AND the copy owns a derived type. assert ( dynamic_cast < Derived *> ( *& poly_copy ));
Note that while deep-destruction of a derived class object from a base class
pointer can be performed with a virtual destructor, the same is not true for
deep-copying.
has no concept of a virtual copy constructor and we are not
proposing its addition. The class template
already implements
deep-destruction without needing virtual destructors; deep-destruction and
deep-copying can be implemented using type-erasure [Impl].
3.3. Pointer constructor
can be constructed from a pointer and optionally a copier
and/or deleter. The
constructed in this manner takes
ownership of the pointer. This constructor is potentially dangerous as a
mismatch in the dynamic and static type of the pointer will result in
incorrectly synthesized copiers and deleters, potentially resulting in slicing
when copying and incomplete deletion during destruction.
class Base { /* functions and members */ }; class Derived : public Base { /* functions and members */ }; Derived * d = new Derived (); Base * p = d ; // static type and dynamic type differ polymorphic_value < Base > poly ( p ); // This copy will have been made using Base’s copy constructor. polymorphic_value < Base > poly_copy = poly ; // Destruction of poly and poly_copy uses Base’s destructor.
While this is potentially error prone, we have elected to trust users with the
tools they are given.
and
have similar constructors
and issues. There are more constructors for
of a less
expert-friendly nature that do not present such dangers including a factory
function
.
Static analysis tools can be written to find cases where static and dynamic
types for pointers passed in to
constructors are not
provably identical.
If the user has not supplied a custom copier or deleter, an exception
is thrown from the pointer-constructor if
the dynamic and static types of the pointer argument do not agree.
In cases where the user has supplied a custom copier and deleter it is assumed
that they will do so to avoid slicing and incomplete destruction: a class
heirarchy with a custom
function and virtual destructor would make use
of
in a user-supplied copier.
3.4. Empty state
presents an empty state as it is desirable for it to be
cheaply constructed and then later assigned. In addition, it may not be
possible to construct the
of a
if it is an abstract
class (a common intended use pattern). While permitting an empty state will
necessitate occasional checks for
,
is intended to
replace the uses of pointers or smart pointers where such checks are also
necessary. The benefits of default constructability (use in vectors and maps)
outweigh the costs of a possible empty state.
A nullable
could be mimicked with
but this is verbose and would require partial template specialization of
to avoid the overhead that would be incurred by a nullable
. But perhaps
the most unfortunate side effect of this approach is that to access the underlying
the
must be dereferenced to get to the
, which then requires
a second dereference to get to the underlying
. This access pattern is undesirable.
3.5. Lack of hashing and comparisons
For a given user-defined type,
, there are multiple strategies to make
hashable and comparable. Without requiring additional
named member functions on the type,
, or mandating that
has virtual
functions and RTTI, the authors do not see how
can
generically support hashing or comparisons. Incurring a cost for functionality
that is not required goes against the 'pay for what you use' philosophy of
.
For a given user-defined type
the user is free to specialize
and implement comparison operators for
.
3.6. Custom copiers and deleters
The resource management performed by
- copying and
destruction of the managed object - can be customized by supplying a _copier_
and _deleter_. If no copier or deleter is supplied then a default copier or
deleter may be used.
A custom copier and deleter are _not_ required, if no custom copier and deleter are provided then the copy constructor and destructor of the managed object will be used.
The default deleter is already defined by the standard library and used by
.
We define the default copier in technical specifications below.
3.7. Allocator Support
Previous revisions of this paper did not support allocators. However, support
is desirable as it offers an opportunity to mitigate the cost of allocations.
There is existing precedence for allocator support of types in the memory
management library;
supports allocators via
, [M. Knejp] suggested the introduction of
to support allocator use with
.
internally uses type-erased to create an internal control
block object responsible for cloning itself and the actual type derived from
.
It is possible to provide different types of control blocks to support allocation
from multiple sources [Impl].
Memory management for
can be fully controlled via the
function which allows passing in an allocator
to control the source of memory. Custom copier and deleter then use the
allocator for allocations and deallocations.
3.8. Design changes from cloned_ptr
The design of
is based upon
(from an early
revision of this paper) and modified following advice from LEWG. The authors
(who unreservedly agree with the design direction suggested by LEWG) would
like to make explicit the cost of these design changes.
has value-like semantics: copies are deep and
is
propagated to the owned object. The first revision of this paper presented
which had mixed pointer/value semantics: copies are deep but
is not propagated to the owned object.
can be built
from
and
but there is no way to remove
propagation from
.
As
is a value,
,
and
are not provided. If a
is constructed with a custom copier or deleter, then there
is no way for a user to implement cast operations like those that are provided
by the standard for
.
3.9. No implicit conversions
Following design feedback,
's constructors have been made
explicit so that surprising implicit conversions cannot take place. Any
conversion to a
must be explicitly requested by user-code.
The converting assignment operators that were present in earlier drafts have also been removed.
For a base class,
, and derived class,
, the
converting assignment
polymorphic_value < DerivedClass > derived ; polymorphic_value < Base > base = derived ;
is no longer valid, the conversion must be made explicit:
polymorphic_value < DerivedClass > derived ; auto base = polymorphic_value < Base > ( derived );
The removal of converting assigments makes
slightly
more verbose to use:
polymorphic_value < Base > base = make_polymorphic_value < DerivedClass > ( args );
is not longer valid and must be written as
auto base = polymorphic_value < Base > ( make_polymorphic_value < DerivedClass > ( args ));
This is somewhat cumbersome so
has been modified to
take an optional extra template argument allowing users to write
polymorphic_value < Base > base = make_polymorphic_value < Base , DerivedClass > ( args );
The change from implicit to explicit construction is deliberately conservative. One can change explicit constructors into implicit constructors without breaking code (other than SFINAE checks), the reverse is not true. Similarly, converting assignments could be added non-disruptively but not so readily removed.
3.10. Impact on the standard
This proposal is a pure library extension. It requires additions to be made to
the standard library header
.
4. Technical specifications
Add the following entry to [tab:support.ft]
Macro NameValue|Header(s)|
|:-:|:-:|:-:|
|
|
|
|
4.1. Additions in [memory.syn] 20.2.2:
// [indirect.value], class template indirect_value template < class T > struct default_copy ; template < class T > struct copier_traits ; template < class T > class polymorphic_value ; template < class T , class ... Ts > constexpr polymorphic_value < T > make_polymorphic_value ( Ts && ... ts ); template < class T , class A = std :: allocator < T > , class ... Ts > constexpr polymorphic_value < T > allocate_polymorphic_value ( A & a , Ts && ... ts ); template < class T > constexpr void swap ( polymorphic_value < T >& p , polymorphic_value < T >& u ) noexcept ;
4.2. X.W Class template copier_traits
[copier.traits]
namespace std { template < class T > struct copier_traits { using deleter_type = * see below * ; }; }
using deleter_type = see below ;
-
Type:
if the qualified-idT :: deleter_type
is valid and denotes a type; otherwise,T :: deleter_type
ifvoid ( * )( U * )
is of the formT
for typesU * ( * )( V )
andU
; otherwise, there is no memberV
.deleter_type
4.3. X.X Class template default_copy
[default.copy]
namespace std { template < class T > struct default_copy { using deleter_type = default_delete < T > ; constexpr default_copy () noexcept = default ; constexpr T * operator ()( const T & t ) const ; }; } // namespace std
The class template
[p1950r1] serves as the default copier for the class
template
.
constexpr T * operator ()( const T & t ) const ;
-
Effects: Equivalent to:
return new T ( t );
4.4. X.Y Class bad_polymorphic_value_construction
[bad_polymorphic_value_construction]
namespace std { class bad_polymorphic_value_construction : public exception { public : bad_polymorphic_value_construction () noexcept = default ; const char * what () const noexcept override ; }; }
Objects of type
are thrown to report
invalid construction of a
.
const char * what () const noexcept override ;
-
Returns: An implementation-defined NTBS.
4.5. X.Z Class template polymorphic_value
[polymorphic_value]
4.5.1. X.Z.1 Class template polymorphic_value
general [polymorphic_value.general]
A
is an object that manages the lifetime of an owned object.
A
object may own objects of different types at different points in its
lifetime. A
object is empty if it has no owned object.
implements value semantics: the owned object (if any) is
copied or destroyed when the
is copied or destroyed.
Copying and destruction of the owned object can be customized by supplying a
copier and a deleter, respectively.
The template parameter
of
shall be a non-union class
type; otherwise the program is ill-formed.
The template parameter
of
may be an incomplete type.
A copier and deleter are said to be _present_ if and only if a
object is constructed from a non-null pointer, or from a
object where a copier and a deleter are present.
4.5.2. X.Z.2 Class template polymorphic_value
synopsis [polymorphic_value.synopsis]
namespace std { template < class T > class polymorphic_value { public : using element_type = T ; // Constructors constexpr polymorphic_value () noexcept ; constexpr polymorphic_value ( nullptr_t ) noexcept ; template < class U > constexpr explicit polymorphic_value ( U && u ); template < class U , class C = default_copy < U > , class D = typename copier_traits < C >:: deleter_type > constexpr explicit polymorphic_value ( U * p , C c = C (), D d = D ()); constexpr polymorphic_value ( const polymorphic_value & p ); template < class U > constexpr explicit polymorphic_value ( const polymorphic_value < U >& p ); constexpr polymorphic_value ( polymorphic_value && p ) noexcept ; template < class U > constexpr explicit polymorphic_value ( polymorphic_value < U >&& p ); // Destructor constexpr ~ polymorphic_value (); // Assignment constexpr polymorphic_value & operator = ( const polymorphic_value & p ); constexpr polymorphic_value & operator = ( polymorphic_value && p ) noexcept ; // Modifiers constexpr void swap ( polymorphic_value & p ) noexcept ; // Observers constexpr const T & operator * () const ; constexpr T & operator * (); constexpr const T * operator -> () const ; constexpr T * operator -> (); constexpr explicit operator bool () const noexcept ; // polymorphic_value specialized algorithms friend constexpr void swap ( polymorphic_value & p , polymorphic_value & u ) noexcept ; }; // polymorphic_value creation template < class T , class U = T , class ... Ts > constexpr polymorphic_value < T > make_polymorphic_value ( Ts && ... ts ); template < class T , class U = T , class A = allocator < U > , class ... Ts > constexpr polymorphic_value < T > allocate_polymorphic_value ( allocator_arg_t , A & a , Ts && ... ts ); } // end namespace std
4.5.3. X.Z.3 Class template polymorphic_value
constructors [polymorphic_value.ctor]
constexpr polymorphic_value () noexcept ; constexpr polymorphic_value ( nullptr_t ) noexcept ;
-
Postconditions:
is empty.* this
template < class U > constexpr explicit polymorphic_value ( U && u );
Let
be
.
-
Constraints:
is true.is_convertible_v < V * , T *>
is true.is_constructible_v < V , U > -
Preconditions:
meets theV
requirements.Cpp17CopyConstructible -
Effects: Constructs a
which owns an object of typepolymorphic_value
, direct-non-list-initialized withV
.std :: forward < U > ( u ) -
Throws: Any exception thrown by the selected constructor of
orV
if required storage cannot be obtained.bad_alloc
template < class U , class C = default_copy < U > , class D = typename copier_traits < C >:: deleter_type > constexpr explicit polymorphic_value ( U * p , C c = C (), D d = D ());
-
Constraints:
is true.is_convertible_v < U * , T *>
If the arguments
and/or
are not supplied, then
and/or
respectively are default constructible types that are not pointer types.
-
Preconditions:
andC
meet theD
andCpp17CopyConstructible
requirements.Cpp17Destructible
If
is non-null then invoking the expression
returns a
non-null
is as if copy constructed from *p. The expression
is well formed, has well-defined behavior, and does not throw exceptions.
Where
, the expression
is well-defined and does
not throw exceptions.
-
Effects: If
is null, creates an empty object otherwise creates an object that owns the objectp
, with a copier of type C initialized from* p
and a deleter of type D initialized fromstd :: move ( c )
.std :: move ( d ) -
Throws:
if required storage cannot be obtained;bad_alloc
ifbad_polymorphic_value_construction
,is_same_v < C , default_copy < U >>
andis_same_v < D , default_delete < U >>
are alltypeid ( * p ) != typeid ( U ) true
. -
Postconditions: If
is null, the empty object created has no copier and no deleter. Otherwise the object created owns the objectp
and has a copier and a deleter present, initialized from* p
andstd :: move ( c )
respectively.std :: move ( d )
template < class U , class A > constexpr explicit polymorphic_value ( U * u , std :: allocator_arg_t , const A & alloc )
-
Constraints:
is true.is_convertible_v < U * , T *> -
Preconditions:
meets the Cpp17Allocator requirements.A -
Effects: Constructs a polymorphic_value that owns the object u. Construction shall use a copy of
to allocate memory for internal use.a -
Throws:
if required storage cannot be obtained;bad_alloc
polymorphic_value ( const polymorphic_value & pv ); template < class U > constexpr explicit polymorphic_value ( const polymorphic_value < U >& pv );
-
Constraints: For the second constructor,
is true.is_convertible_v < U * , T *> -
Effects: If
is empty, constructs an empty object. Otherwise creates an object that owns a copy of the object managed bypv
. If a copier and deleter are present inpv
then the copy is created by the copier inpv
. Otherwise the copy is created by copy construction of the owned object. If a copier and deleter are present inpv
then the copier and deleter of the object constructed are copied from those inpv
so that they are present and of typepv
andC
respectively.D -
Postconditions:
.bool ( * this ) == bool ( pv ) -
Throws: Any exception thrown by invocation of the copier, copying the copier and deleter, or
if required storage cannot be obtained.bad_alloc
polymorphic_value ( polymorphic_value && pv ) noexcept ; template < class U > constexpr explicit polymorphic_value ( polymorphic_value < U >&& pv );
-
Constraints: For the second constructor,
is true.is_convertible_v < U * , T *> -
Effects: If
is empty, constructs an empty object. Otherwise the object owned bypv
is transferred to the constructed object. If a copier and deleter are present inpv
then the copier and deleter are transferred to the constructed object so that they are present and of typepv
andC
respectively.D -
Postconditions:
owns the object previously owned by* this
(if any).pv
is empty.pv
4.5.4. X.Z.4 Class template polymorphic_value
destructor [polymorphic_value.dtor]
constexpr ~ polymorphic_value ();
-
Effects: If a copier
and a deleterc
are present, evaluatesd
and destroysd ( operator -> ())
andc
. Otherwise destroys the owned object (if any) and frees its storage.d
4.5.5. X.Z.5 Class template polymorphic_value
assignment [polymorphic_value.assignment]
constexpr polymorphic_value & operator = ( const polymorphic_value & pv );
-
Effects: Equivalent to
. No effects if an exception is thrown.polymorphic_value ( pv ). swap ( * this ) -
Throws: Any exception thrown by the copier or
if required storage cannot be obtained.bad_alloc -
Returns:
.* this -
Postconditions: The state of
is as if copy constructed from* this
.pv
constexpr polymorphic_value & operator = ( polymorphic_value && pv ) noexcept ;
-
Effects: Equivalent to
.polymorphic_value ( std :: move ( pv )). swap ( * this ) -
Returns:
.* this -
Postconditions: The state
is equivalent to the original state of* this
.pv
4.5.6. X.Z.6 Class template polymorphic_value
modifiers [polymorphic_value.modifiers]
constexpr void swap ( polymorphic_value & p ) noexcept ;
-
Constraints: is_is_nothrow_swappablev
is true. is_is_nothrow_swappablev is true. -
Effects: Invokes swap on the stored pointers and on the stored copiers and deleters of *this and
.p
4.5.7. X.Z.7 Class template polymorphic_value
observers [polymorphic_value.observers]
constexpr const T & operator * () const ; constexpr T & operator * ();
-
Preconditions:
isbool ( * this ) true
. -
Returns: A reference to the owned object.
constexpr const T * operator -> () const ; constexpr T * operator -> ();
-
Preconditions:
isbool ( * this ) true
. -
Returns: A pointer to the owned object.
explicit operator bool () const noexcept ;
-
Returns:
false
if the
is empty, otherwisepolymorphic_value true
.
4.5.8. X.Z.8 Class template polymorphic_value
creation [polymorphic_value.creation]
template < class T , class U = T , class ... Ts > constexpr polymorphic_value < T > make_polymorphic_value ( Ts && ... ts );
-
Constraints:
is true.is_constructible_v < U , Ts ... > -
Preconditions:
meets theU
requirements.Cpp17CopyConstructible -
Returns: A
owning an object of typepolymorphic_value < T >
direct-non-list-initialized withU
.std :: forward < Ts > ( ts )...
[Note: Implementations are encouraged to avoid multiple allocations. - end note]
template < class T , class U = T , class A = allocator < U > , class ... Ts > constexpr polymorphic_value < T > allocate_polymorphic_value ( const A & a , Ts && ... ts );
-
Constraints:
is true.is_constructible_v < U , Ts ... > -
Preconditions:
meets theU
requirements.Cpp17CopyConstructible -
Effects: Constructs a polymorphic_value that shall use a copy of
to allocate memory for internal use.a -
Returns: A
owning an object of typepolymorphic_value < T >
direct-non-list-initialized withU
.std :: forward < Ts > ( ts )...
4.5.9. X.Z.9 Class template polymorphic_value
specialized algorithms [polymorphic_value.spec]
friend constexpr void swap ( polymorphic_value & p , polymorphic_value & u ) noexcept ;
-
Effects: Equivalent to
.p . swap ( u )
5. Acknowledgements
The authors would like to thank Maciej Bogus, Matthew Calabrese, Casey Carter, Germán Diago, Louis Dionne, Bengt Gustafsson, Tom Hudson, Stephan T Lavavej, Tomasz Kamiński, David Krauss, Thomas Koeppe, LanguageLawyer, Nevin Liber, Nathan Myers, Roger Orr, Geoff Romer, Patrice Roy, Tony van Eerd and Ville Voutilainen for suggestions and useful discussion.
The authors would also extend thanks for contributions to the reference implementation which has driven the design, including Anthony Williams, Ed Catmur, and Marcell Kiss.
5.1. References
[N3339] "A Preliminary Proposal for a Deep-Copying Smart Pointer", W.E.Brown, 2012
[p1950r1] indirect_value: A Free-Store-Allocated Value Type For C++
[P0302r1] "Removing Allocator support in std::function", Jonathan Wakely
[M. Knejp] P0316R0: allocate_unique and allocator_delete
[S.Parent] "C++ Seasoning", Sean Parent, 2013
[Impl] Reference implementation: polymorphic_value, J.B.Coe
[P0302r1] "Removing Allocator support in std::function", Jonathan Wakely