1. Abstract
Add a class template,indirect_value 2. Change history
Changes in P1950r1
- 
     Add design discussion. 
- 
     Add comparison with unique_ptr polymorphic_value 
- 
     Add node-based container and hot-cold splitting as further motivational examples. 
- 
     Add examples of similar classes already in use. 
3. Introduction
The class template,indirect_value indirect_value indirect_value T indirect_value < T > const T indirect_value 3.1. Motivation
It may be desirable for a class member to be an incomplete type or for the storage used by a member object to be separate from the class itself. In both such cases, the member would traditionally be represented as pointer:
class MyClass { AType data_member_ ; AnotherType * indirect_data_member_ ; }; 
The author of such a class will need to implement special member functions as the compiler-generated special member functions will not function correctly (indirect data will not be copied, assigned to or deleted, only the pointer).
Special care will be needed when using the class in multithreaded environments. An instance of 
The class template 
3.1.1. Node-based containers
A tree or linked list can be implemented as a node-based container. This gives stable references to data in the nodes at the cost of memory indirection when accessing data.
Nodes would typically contain data and pointer(s) to other nodes so that the list or tree can be navigated:
class ForwardListNode { int data_ ; ForwardListNode * next_ ; }; class ForwardList { ForwardListNode * head_ ; public : // ... }; 
The special member functions of 
Care must be taken when implementing 
Implementing the ForwardList and ForwardListNode with 
class ForwardListNode { int data_ ; indirect_value < ForwardListNode > next_ ; }; class ForwardList { indirect_value < ForwardListNode > head_ ; public : // ... }; 
Compiler-generated special member functions will behave correctly and do not need to be written manually. 
3.1.2. Hot-cold splitting
When working with collections of data, CPU caches are often under-utilized when algorithms access certain data members much more frequently than others. This results in some cache lines becoming busier than others, prefetchers preemptively caching memory which is later evicted without being used by the CPU or memory bandwidth becoming a limiting factor in performance. In such cases segregating data structures in terms of hot and cold data can remove pressure on system resources.
class Element { SmallData frequently_accessed_data ; LargeData infrequently_accessed_data ; }; vector < Element > elements ; auto active = find ( elements . begin (), elements . end (), []( const auto & e ) { return e . frequently_accessed_data . active (); }; 
In such cases adding a level of indirection to the larger, less frequently-accessed data can relieve bandwidth
pressure [C. Ericson].  Using 
class Element { SmallData frequently_accessed_data ; indirect_value < LargeData > infrequently_accessed_data ; }; vector < Element > elements ; auto active = find ( elements . begin (), elements . end (), []( const auto & e ) { return e . frequently_accessed_data . active (); }; 
3.1.3. Pointer to Implementation - PImpl
In C++, when anything in a class definition changes, dependent classes require recompilation. As early as 1992 the
Handle/Body idiom was suggested to break this dependency [J. Coplien]. From this pattern, the PImpl idiom was 
specialised [H. Sutter].  Despite its relative maturity, using the PImpl idiom requires careful thought about copying 
and 
// Header file class widget { public : widget (); ~ widget (); private : class impl ; std :: unique_ptr < impl > pimpl_ ; }; 
// Implementation file class widget :: impl { // ::: }; widget :: widget () : pimpl_ { std :: make_unique < impl > ( /*...*/ } { } // Destructor needs to be defined in a the same TU as <code data-opaque bs-autolink-syntax='`widget::impl`'>widget::impl</code>. widget ::~ widget () = default ; 
For convenience, the widget class will be referred to as the “visible class” and impl class the “
3.1.3.1. Issues with const-propagation
Usingstd :: unique_ptr const const unique_ptr The compiler is unable to make thread-compatibility assumptions for 
The desired semantics of a PImpl-owner are value-like, like those of 
3.1.3.2. Issues with copies
The copy-constructor and copy-assignment operator ofstd :: unique_ptr std :: unique_ptr 3.1.3.3. An indirect_value 
    Using indirect_value const const // Header file class widget { public : widget (); widget ( widget && rhs ) noexcept ; widget ( const widget & rhs ); widget & operator = ( widget && rhs ) noexcept ; widget & operator = ( const widget & rhs ); ~ widget (); private : class impl ; std :: indirect_value < impl >> pimpl ; }; 
// Implementation file class widget :: impl { // ::: }; // Special-member functions need to be defined in a the same TU as <code data-opaque bs-autolink-syntax='`widget::impl`'>widget::impl</code>. widget :: widget ( widget && rhs ) noexcept = default ; widget :: widget ( const widget & rhs ) = default ; widget & widget :: operator = ( widget && rhs ) noexcept = default ; widget & widget :: operator = ( const widget & rhs ) = default ; widget ::~ widget () = default ; 
3.2. Design decisions
3.2.1. Should there be be an empty state?
There is a design decision to be made about the default constructor
of 
If 
If the default constructed state of 
Allowing an empty state for 
A nullable 
As designed, 
3.2.2. Is there a small object optimization?
A small object optimization is permissible for 
It would be possible for the size of the small buffer to be specified as a template argument.
Such a design would be inconsistent with the broader Standard Library design: 
3.2.3. How are allocators supported?
It may be desirable for users to control the way memory is managed by an instance of a class.
Classes such as 
Like 
The in-place constructor of 
Memory management for 
3.3. Prior Art
There have been previous proposal for deep-copying smart pointers that proposed copy semantics [W. Brown].cloned_ptr polymorphic_value This paper is not unique in these ideas. GitHub code search finds 602k lines of code referencing "PImpl" and 99 C ++ repositories claiming to provide generic implementations of Pimpl. Additionally other authors have addressed this topic [A. Upadyshev]. Some generic implementations of note in the wild are:
| Project | Link | Deep Copying | Const Propagation | 
|---|---|---|---|
| Boost Pimpl | https://github.com/sean-/boost-pimpl | Yes | Yes | 
| pimpl | https://github.com/JonChesterfield/pimpl | Yes | No | 
| impl_ptr | https://github.com/sth/impl_ptr | Yes | Yes | 
| Simpl | https://github.com/oliora/samples/blob/master/spimpl.h | Yes | Yes | 
| smart_pimpl | https://github.com/SBreezyx/smart_pimpl | No | No | 
| pimpl_on_stack | https://github.com/kuznetsss/pimpl_on_stack/ | Yes | Yes | 
| deep_const_ptr | https://github.com/torbjoernk/deep_const_ptr | No | Yes | 
Divergence on issues such as deep copying and const propagation along with subtleties in implementation mean that a single standardized solution would be of broad benefit to the community of C++ users.
3.4. Completeness of T
Smart pointer types in the Standard Library expect that some of the members can be instantiated with incomplete types [H.Hinnant]. Similarly, this is the case forindirect_value | Method | Description | Incomplete/Complete | 
|---|---|---|
|  | Default constructor | Incomplete | 
|  | Copy-constructor | Complete | 
|  | Move-constructor | Incomplete | 
|  | Destructor | Complete | 
|  | Copy-assignment | Complete | 
|  | Move-assignment | Complete | 
|  | Indirection-operator | Incomplete | 
|  | Indirection-operator | Incomplete | 
|  | Member-of-pointer-operator | Incomplete | 
|  | Member-of-pointer-operator | Incomplete | 
|  | Bool-operator | Incomplete | 
|  | Swap | Incomplete | 
3.5. Comparison with unique_ptr < T > polymorphic_value < T > 
   The class template 
| Behaviour |  |  |  | 
|---|---|---|---|
| Default constructed state | Empty | Empty | Empty | 
| Copyable | Yes | Yes | No | 
| Const-propagating | Yes | Yes | No | 
| Polymorphic | No | Yes | Yes | 
| Customizable memory access | Yes | No | Yes | 
3.6. Impact on the standard
This proposal is a pure library extension. It requires additions to be made to the standard library header< memory > 4. Technical specifications
4.1. X.X Class template default_copy 
The class template default_copy serves as the default copier for the class templatenamespace std { template < class T > struct default_copy { T * operator ()( const T & t ) const ; }; } // namespace std 
indirect_value T default_copy - 
     Returns: new T ( t ); 
4.2. X.Y Class template indirect_value 
   4.2.1. X.Y.1 Class template indirect_value 
    An indirect_value v p p v v p An 
Copying a non-empty 
Copying from an empty 
Copying and disposal of the owned object can be customised by supplying a copier and deleter.
The template parameter 
The template parameter 
[Note: Implementations are encouraged to avoid the use of dynamic memory for ownership of small objects.]
4.2.2. X.Y.2 Class template indirect_value 
template < class T , class C = std :: default_copy < T > , class D = std :: default_delete < T >> class indirect_value { public : using value_type = T ; // Constructors constexpr indirect_value () noexcept ; explicit indirect_value ( T * p , C c = C {}, D d = D {}); indirect_value ( const indirect_value & p ); indirect_value ( indirect_value && p ) noexcept ; template < class ...Ts > indirect_value ( std :: in_place_t , Ts && ... ts ); // See below // Destructor ~ indirect_value (); // Assignment indirect_value & operator = ( const indirect_value & p ); indirect_value & operator = ( indirect_value && p ) noexcept ; // Modifiers void swap ( indirect_value < T >& p ) noexcept ; // Observers T & operator * (); T * operator -> (); const T & operator * () const ; const T * operator -> () const ; explicit operator bool () const noexcept ; }; // indirect_value creation template < class T , class ...Ts > indirect_value < T > make_indirect_value ( Ts && ... ts ); // See below // indirect_value specialized algorithms template < class T > void swap ( indirect_value < T >& p , indirect_value < T >& u ) noexcept ; } // end namespace std 
4.2.3. X.Y.3 Class template indirect_value 
   - 
     Remarks: The method shall work with incomplete pointer type for T 
- 
     Effects: Constructs an empty indirect_value - 
       Postconditions: bool ( * this ) == false
 
- 
       
- 
     Effects: Creates an indirect_value p p indirect_value c d 
- 
     Requires: C D p c ( * p ) T * d ( p ) 
- 
     Postconditions: bool ( * this ) == bool ( p ) 
- 
     Remarks: A custom copier and deleter are said to be ‘present’ in a indirect_value 
- 
     Constraints: is_copy_constructible_v < T > 
- 
     Effects: Creates a indirect_value p p p indirect_value p 
- 
     Throws: Any exception thrown by the copier or bad_alloc 
- 
     Postconditions: bool ( * this ) == bool ( p ) 
- 
     Effects: Move-constructs an indirect_value p p indirect_value p 
- 
     Postconditions: * this p p 
- 
     Remarks: The method shall work with incomplete pointer type for T 
- 
     Effects: Constructs an indirect_value T std :: forward < Ts > ( ts )... 
- 
     Throws: Any exception thrown by the selected constructor of T bad_alloc 
- 
     Requires is_same_v < C , default_copy > && is_same_v < D , default_delete > 
4.2.4. X.Y.4 Class template indirect_value 
    ~ indirect_value (); - 
     Effects: If get () == nullptr d d ( p ) 
4.2.5. X.Y.5 Class template indirect_value 
    indirect_value &  operator = ( const  indirect_value &  p ); - 
     Constraints: is_copy_assignable < T > 
- 
     Effects: * this p p p * this p * this p 
- 
     Throws: Any exception thrown by the copier or bad_alloc 
- 
     Returns: * this 
- 
     Postconditions: bool ( * this ) == bool ( p ) 
- 
     Effects: Ownership of the resource managed by p p * this p 
- 
     Returns: * this 
- 
     Postconditions: * this p p 
4.2.6. X.Y.6 Class template indirect_value 
    void  swap ( indirect_value &  p )  noexcept ; - 
     Effects: Exchanges the contents of p * this 
- 
     Remarks: The method shall work with incomplete pointer type for T 
4.2.7. X.Y.7 Class template indirect_value 
T & operator * (); const T & operator * () const ; 
- 
     Requires: bool ( * this ) 
- 
     Returns: A reference to the owned object. 
T * operator -> () noexcept ; const T * operator -> () const noexcept ; 
- 
     Requires: bool ( * this ) 
- 
     Returns: A pointer to the owned object. 
- 
     Remarks: The method shall work with incomplete pointer type for T 
- 
     Returns: false if the indirect_value 
- 
     Remarks: The method shall work with incomplete pointer type for T 
4.2.8. X.Z.8 Class template indirect_value 
template < class T , class U = T , class ...Ts > indirect_value < T > make_indirect_value ( Ts && ... ts ); 
- 
     Constraints: is_constructible_v < U , Ts ... > 
- 
     Expects: U Cpp17CopyConstructible 
- 
     Returns: A indirect_value < T > std :: forward < Ts > ( ts )... 
- 
     Requires is_same_v < C , default_copy > && is_same_v < D , default_delete > 
5. Acknowledgements
The authors would like to thank Thomas Russell, and Andrew Bennieston for useful discussions on the topic and the BSI panel for on-going support.6. References
[J. Coe] p0201r3: A polymorphic value-type for C++
[J. Coplien] Advanced C++ Programming Styles and Idioms (Addison-Wesley), James O. Coplien, 1992
[C. Ericson] Memory Optimization, Christer Ericson, Games Developers Conference, 2003
[A. Upadyshev] PIMPL, Rule of Zero and Scott Meyers, Andrey Upadyshev, 2015
[H. Hinnant] “Incomplete types and shared_ptr / unique_ptr”, Howard Hinnant, 2011
[H. Sutter] "Pimpls - Beauty Marks You Can Depend On", Herb Sutter, 1998
[Impl] Reference implementation: indirect_value, J.B.Coe
[S. Meyers] Effective Modern C++, Item 22: When using the Pimpl Idiom, define special member functions in the implementation file, Scott Meyers, 2014
[W. Brown] n3339: A Preliminary Proposal for a Deep-Copying Smart Pointer, Walter E. Brown, 2012