1. Revision History
1.1. Revision 0
Initial release.
1.2. Revision 1
Add wording. Incorporate wording feedback. Eliminate CTAD design. Add a few more words about implementation experience.
2. Motivation
We have very good tools for handling unique and shared resource semantics, alongside more coming with Intrusive Smart Pointers. Independently between several different companies, studios, and shops -- from VMWare and Microsoft to small game development startups -- a common type has been implemented. It has many names: 
This paper is a culmination of a private survey of types from the industry to propose a common, future-proof, high-performance 
In short: it’s a thing convertible to a 
3. Design Considerations
The core of 
3.1. Synopsis
The function template’s full specification is:
namespace std { template < class Pointer , class Smart , class ... Args > auto out_ptr ( Smart & s , Args && ... args ) noexcept -> out_ptr_t < Smart , Pointer , Args ... > ; template < class Smart , class ... Args > auto out_ptr ( Smart & s , Args && ... args ) noexcept -> decltype ( out_ptr < PointerOf < Smart >> ( s , std :: forward < Args > ( args )...)); template < class Pointer , class Smart , class ... Args > auto inout_ptr ( Smart & s , Args && ... args ) noexcept -> inout_ptr_t < Smart , Pointer , Args ... > ; template < class Smart , class ... Args > auto inout_ptr ( Smart & s , Args && ... args ) noexcept -> decltype ( inout_ptr < PointerOf < Smart >> ( s , std :: forward < Args > ( args )...)); } 
Where 
template < class Smart , class Pointer , class ... Args > struct out_ptr_t { out_ptr_t ( Smart & , Args ...); ~ out_ptr_t () noexcept ; operator Pointer * () noexcept ; operator void ** () noexcept ; }; template < class Smart , class Pointer , class ... Args > struct inout_ptr_t { inout_ptr_t ( Smart & , Args ...); ~ inout_ptr_t () noexcept ; operator Pointer * () noexcept ; operator void ** () noexcept ; }; 
We specify "at minimum" because we expect users to override this type for their own shared, unique, handle-alike, reference-counting, and etc. smart pointers. The destructor of 
3.2. Header and Feature Macro
The target header is 
3.3. Overview
error_num c_api_create_handle ( int seed_value , int ** p_handle ); error_num c_api_re_create_handle ( int seed_value , int ** p_handle ); void c_api_delete_handle ( int * handle ); struct resource_deleter { void operator ()( int * handle ) { c_api_delete_handle ( handle ); } }; 
Given a smart pointer, it can be used like so:
std :: unique_ptr < int , resource_deleter > resource ( nullptr ); error_num err = c_api_create_handle ( 24 , std :: out_ptr ( resource ) ); if ( err == C_API_ERROR_CONDITION ) { // handle errors } // resource.get() the out-value from the C API function 
Or, in the re-create (reallocation) case:
std :: unique_ptr < int , resource_deleter > resource ( nullptr ); error_num err = c_api_re_create_handle ( 24 , std :: inout_ptr ( resource ) ); if ( err == C_API_ERROR_CONDITION ) { // handle errors } // resource.get() the out-value from the C API function 
3.4. Safety
This implementation uses a pack of 
std :: shared_ptr < int > resource ( nullptr ); error_num err = c_api_create_handle ( 24 , std :: out_ptr ( resource , resource_deleter {}) ); if ( err == C_API_ERROR_CONDITION ) { // handle errors } // resource.get() the out-value from // the C API function 
Additional arguments past the smart pointer stored in 
Of importance here is to note that 
std :: shared_ptr < int > resource ( nullptr ); error_num err = c_api_create_handle ( 42 , std :: out_ptr ( resource ) ); // ERROR: deleter was changed // to an equivalent of // std::default_delete! 
It is likely the intent of the programmer to also pass the fictional 
3.5. Casting Support
There are also many APIs (COM-style APIs, base-class handle APIs, type-erasure APIs) where the initialization requires that the type passed to the function is of some fundamental (
It is also important to note that going in the opposite direction is also highly desirable, especially in the case of doing API-hiding behind an e.g. 
For example, consider this DirectX Graphics Infrastructure Interface (DXGI) function on 
HRESULT EnumAdapterByGpuPreference ( UINT Adapter , DXGI_GPU_PREFERENCE GpuPreference , REFIID riid , void ** ppvAdapter ); 
Using 
HRESULT result = dxgi_factory . EnumAdapterByGpuPreference ( 0 , DXGI_GPU_PREFERENCE_MINIMUM_POWER , IID_IDXGIAdapter , std :: out_ptr < void *> ( adapter ) ); if ( FAILED ( result )) { // handle errors } // adapter.get() contains strongly-typed pointer 
No manual casting, 
3.6. Reallocation Support
In some cases, a function given a valid handle/pointer will delete that pointer on your behalf before performing an allocation in the same pointer. In these cases, just 
This can be heavily optimized in the case of 
4. Implementation Experience
This library has been brewed at many companies in their private implementations, and implementations in the wild are scattered throughout code bases with no unifying type. As noted in §2 Motivation, Microsoft has implemented this in 
The primary author of [p0468] in pre-r0 days also implemented an overloaded 
Given that many companies, studios and individuals have all invented the same type independently of one another, we believe this is a strong indicator of agreement on an existing practice that should see a proposal to the standard.
A full implementation with UB and friendly optimizations is available in the repository. The type has been privately used in many projects over the last four years, and this public implementation is already seeing use at companies today. It has been particularly helpful with many COM APIs, and the re-allocation support in 
4.1. Why Not Wrap It?
A common point raised while using this abstraction is to simply "wrap the target function". We believe this to be a non-starter in many cases: there are thousands of C API functions and even the most dedicated of tools have trouble producing lean wrappers around them. This tends to work for one-off functions, but suffers scalability problems very quickly.
Templated intermediate wrapper functions which take a function, perfectly forwards arguments, and attempts to generate e.g. a 
5. Performance
Many C programmers in our various engineering shops and companies have taken note that manually re-initializing a 
Teams eager to squeeze out performance realize they can only do this by relying on type-punning shenanigans to extract the actual value out of 
Below are some graphs indicating the performance metrics of the code. 5 categories were measured:
- 
     "c_code": handwritten C code, which does not use this idiom 
- 
     "clever": uses UB to alias the pointer value stored in std :: unique_ptr 
- 
     "friendly": modifies VC++'s, libc++'s, and libstdc++'s std :: unique_ptr out_ptr 
- 
     "manual": does the work by-hand using reset/release from a std :: unique_ptr 
- 
     "simple": a out_ptr 
The full JSON data for these benchmarks is available in the repository, as well as all of the code necessary to run the benchmarks across all platforms with a simple CMake build system.
5.1. For std :: out_ptr 
   You can observe two graphs for two common 
5.2. For std :: inout_ptr 
   The speed increase here is even more dramatic: reseating the pointer through 
6. Bikeshed
As with every proposal, naming, conventions and other tidbits not related to implementation are important. This section is for pinning down all the little details to make it suitable for the standard.
6.1. Alternative Specification
The authors of this proposal know of two ways to specify this proposal’s goals.
The first way is to specify both functions 
The second way is to specify the class names to be 
Part of this specification is that you can specify the stored pointer for the underlying implementation of 
The authors have settled on the approach in §3.1 Synopsis. We believe this is the most robust and easiest to use: singular names tend to be easier to teach and use for both programmers and tools.
6.2. Naming
Naming is hard, and therefore we provide a few names to duke it out in the Bikeshed Arena:
For the 
- 
     out_ptr 
- 
     c_ptr 
- 
     c_out_ptr 
- 
     out_c_ptr 
- 
     alloc_c_ptr 
- 
     out_smart 
- 
     ptrptr 
- 
     ptr_to_ptr 
- 
     ptr_to_smart 
- 
     ptr_ref 
For the 
- 
     inout_ptr 
- 
     c_in_ptr 
- 
     c_inout_ptr 
- 
     inout_c_ptr 
- 
     realloc_c_ptr 
- 
     inout_smart, 
- 
     realloc_ptr_to_ptr 
- 
     realloc_ptr_to_smart 
- 
     realloc_ptr_ref 
As a pairing, 
7. Proposed Changes
The following wording is for the Library section, relative to [n4762]. This feature will go in the 
7.1. Proposed Feature Test Macro and Header
This should be available with the rest of the smart pointers, and thusly be included by simply including 
The proposed feature test macro for this is 
7.2. Intent
The intent of this wording is to allow implementers the freedom to implement the return type from 
- 
     the return type is of the name inout_ptr_t out_ptr_t 
- 
     the destructor of inout_ptr_t out_ptr_t out_ptr 
- 
     the proper implicit conversion operators are added to the type, 
- 
     the standard library implementation itself does not specialize inout_ptr_t out_ptr_t 
- 
     std :: shared_ptr out_ptr inout_ptr . reset () 
- 
     std :: shared_ptr inout_ptr_t 
The goals of the wording are to not restrict implementation strategies (e.g., a 
7.3. Proposed Wording
Modify §19.10.1 In general [memory.general] as follows:
1 The header
defines several types and function templates that describe properties of pointers and pointer-like types, manage memory for containers and other template types, destroy objects, and construct multiple objects in uninitialized memory buffers (19.10.3–19.10.11). The header also defines the templates unique_ptr, shared_ptr, weak_ptr, out_ptr_t, inout_ptr_t, and various function templates that operate on objects of these types (19.11). Add §19.10.2 Definitions [memory.defns] as follows:
1 Definition: Let
denote a type that is:POINTER_OF ( T ) 
- — the type of
if the qualified-idT :: pointer is valid and denotes a type, orT :: pointer - — the type of
if the qualified-idstd :: add_pointer_t < typename T :: element_type > is valid and denotes a type, orT :: element_type - — the type of
ifstd :: add_pointer_t < typename std :: pointer_traits < T >:: element_type > is valid,std :: pointer_traits < T >:: element_type - — otherwise,
decltype ( addressof ( * declval < T > ())) 
Add to §19.10.3 (previously §19.10.2) Header 
// 19.11.9, out_ptr_t template < class Smart , class Pointer , class ... Args > struct out_ptr_t ; // 19.11.10, out_ptr template < class Pointer , class Smart , class ... Args > auto out_ptr ( Smart & s , Args && ... args ) noexcept -> out_ptr_t < Smart , Pointer , Args ... > ; template < class Smart , class ... Args > auto out_ptr ( Smart & s , Args && ... args ) noexcept -> decltype ( out_ptr < PointerOf < Smart >> ( s , std :: forward < Args > ( args )...)); // 19.11.11, inout_ptr_t template < class Smart , class Pointer , class ... Args > struct inout_ptr_t ; // 19.11.12, inout_ptr template < class Pointer , class Smart , class ... Args > inout_ptr_t < Smart , Pointer , Args ... > inout_ptr ( Smart & s , Args && ... args ) noexcept ; template < class Smart , class ... Args > auto inout_ptr ( Smart & s , Args && ... args ) noexcept -> decltype ( inout_ptr < PointerOf < Smart >> ( s , std :: forward < Args > ( args )...)); 
Insert §19.11.9 [out_ptr.class]:
19.11.9 Class Template[out_ptr.class]out_ptr_t 1 out_ptr_t is a type used with smart pointers (19.11) and types which are designed on the same principles to interoperate easily with functions that use output pointer parameters. [ Note — For example, a function of the form
— end note ].void foo ( void ** ) 2 out_ptr_t may be specialized (12.6.5) for user-defined types and shall meet the observable behavior in the rest of this section.
namespace std { template < class Smart , class Pointer , class ... Args > struct out_ptr_t { // 19.11.9.1, constructors out_ptr_t ( Smart & , Args ...) noexcept ; out_ptr_t ( out_ptr_t && ) noexcept ; out_ptr_t ( const out_ptr_t & ) noexcept = delete ; // 19.11.9.2, assignment out_ptr_t & operator = ( out_ptr_t && ) noexcept ; out_ptr_t & operator = ( const out_ptr_t & ) noexcept = delete ; // 19.11.9.3, destructors ~ out_ptr_t (); // 19.11.9.4, conversion operators operator Pointer * () noexcept ; // if Pointer not void* operator void ** () noexcept ; private : Smart * s ; // exposition only tuple < Args > a ; // exposition only Pointer p ; // exposition only }; } 2 If
is a specialization ofSmart andshared_ptr , the program is ill-formed.sizeof ...( Args ) == 0 shall meet thePointer requirements (15.5.3.3).Cpp17NullablePointer 3 [ Note: It is typically a user error to reset a
without specifying a deleter, asshared_ptr will replace a custom deleter with the default deleter upon usage ofstd :: shared_ptr , as specified in 19.11.3.4. — end Note ]. reset () 19.11.9.1 Constructors [out_ptr.class.ctor]
out_ptr_t ( Smart & smart , Args ... args ) noexcept ; 1 Effects: constructs an object of
and stores the arguments to be used for destructor.out_ptr_t 2 Equivalent to:
out_ptr_t ( Smart & smart , Args ... args ) noexcept : s ( & smart ), a ( std :: forward < Args > ( args )...), p ( static_cast < Pointer > ( smart . get ())) {}. 
out_ptr_t ( out_ptr && rhs ) noexcept ; 3 Effects: moves all elements of
intorhs , then sets* this torhs . p so that the destructor’s effects do not apply.nullptr 4 Equivalent to:
out_ptr_t ( out_ptr_t && rhs ) noexcept : s ( std :: move ( rhs . s )), a ( std :: move ( rhs . a )), p ( std :: move ( rhs . p )) { rhs . p = nullptr ; } 19.11.9.2 Assignment [out_ptr.class.assign]
out_ptr_t & operator = ( out_ptr && rhs ) noexcept ; 1 Effects: moves each element of
intorhs , then sets* this torhs . p so that the destructor’s effects do not apply.nullptr 2 Equivalent to:
out_ptr_t & operator = ( out_ptr_t && rhs ) noexcept { s = std :: move ( rhs . s ); a = std :: move ( rhs . a ); p = std :: move ( rhs . p ); rhs . p = nullptr ; return * this ; } 19.11.9.3 Destructors [out_ptr.class.dtor]
~ out_ptr_t (); 1 Let
beSP (19.10.2).POINTER_OF ( Smart ) 2 Effects: reset the pointer stored in s, if p is not null using the
value stored inArgs , if any.* this 3 Equivalent to:
- —
ifif ( p != nullptr ) { s . reset ( static_cast < SP > ( p ), std :: forward < Args > ( args )... ); } is a valid member function onreset ,Smart - — otherwise
;if ( p != nullptr ) { s = Smart ( static_cast < SP > ( p ), std :: forward < Args > ( args )... ); } 19.11.9.4 Conversions [out_ptr.class.conv]
operator Pointer * () noexcept ; operator void ** () noexcept ; // if Pointer not void* 1 Constraints: The second conversion shall participate in conversion if
is not of typePointer .void * 2 Effects: The first conversion returns a pointer to
. The second conversion returnp static_cast < void **> ( static_cast < void *> ( static_cast < Pointer *> ( * this ))); 
Insert §19.11.10 [out_ptr]:
19.11.10 Function Template
[out_ptr]out_ptr 1 out_ptr is a function template that produces an object of type out_ptr_t (19.11.9).
namespace std { template < class Pointer , class Smart , class ... Args > auto out_ptr ( Smart & s , Args && ... args ) noexcept -> out_ptr_t < Smart , Pointer , Args ... > ; template < class Smart , class ... Args > auto out_ptr ( Smart & s , Args && ... args ) noexcept -> decltype ( out_ptr < Pointer > ( s , std :: forward < Args > ( args )...)); } 2 Effects: For the second overload, let
bePointer (19.10.2).POINTER_OF ( Smart ) 3 Equivalent to:
return out_ptr_t < Smart , Pointer , Args ... > ( s , std :: forward < Args > ( args )...); 
Insert §19.11.11 [inout_ptr.class]:
19.11.11 Class Template[inout_ptr.class]inout_ptr_t 1 inout_ptr_t is a type used with smart pointers (19.11) and types which are designed on the same principles to interoperate easily with functions that use output pointer parameters. [ Note — For example, a function of the form
— end note ].void foo ( void ** ) 2 inout_ptr_t may be specialized (12.6.5) for user-defined types and shall meet the observable behavior in the rest of this section.
namespace std { template < class Smart , class Pointer , class ... Args > struct inout_ptr_t { // 19.11.11.1, constructors inout_ptr_t ( Smart & , Args ...) noexcept ; inout_ptr_t ( inout_ptr_t && ) noexcept ; inout_ptr_t ( const inout_ptr_t & ) noexcept = delete ; // 19.11.11.2, assignment inout_ptr_t & operator = ( inout_ptr_t && ) noexcept ; inout_ptr_t & operator = ( const inout_ptr_t & ) noexcept = delete ; // 19.11.11.3, destructors ~ inout_ptr_t (); // 19.11.11.4, conversion operators operator Pointer * () noexcept ; // if Pointer not void* operator void ** () noexcept ; private : Smart * s ; // exposition only tuple < Args > a ; // exposition only Pointer p ; // exposition only }; } 2 If
is a specialization ofSmart andshared_ptr , the program is ill-formed.sizeof ...( Args ) == 0 shall meet thePointer requirements (15.5.3.3).Cpp17NullablePointer 3 [ Note: It is typically a user error to reset a
without specifying a deleter, asshared_ptr will replace a custom deleter with the default deleter upon usage ofstd :: shared_ptr , as specified in 19.11.3.4. — end Note ]. reset () 19.11.11.1 Constructors [inout_ptr.class.ctor]
inout_ptr_t ( Smart & smart , Args ... args ) noexcept ; 1 Constraints:
must be a valid expression.s . release () 1 Effects: constructs an object of
and stores the arguments to be used for destructor.inout_ptr_t 2 Equivalent to:
inout_ptr_t ( Smart & smart , Args ... args ) noexcept : s ( & smart ), a ( std :: forward < Args > ( args )...), p ( static_cast < Pointer > ( smart . release ())) {}. 
inout_ptr_t ( inout_ptr && rhs ) noexcept ; 3 Effects: moves all elements of
intorhs , then sets* this torhs . p so that the destructor’s effects do not apply.nullptr 4 Equivalent to:
inout_ptr_t ( inout_ptr_t && rhs ) noexcept : s ( std :: move ( rhs . s )), a ( std :: move ( rhs . a )), p ( std :: move ( rhs . p )) { rhs . p = nullptr ; } 19.11.11.2 Assignment [inout_ptr.class.assign]
inout_ptr_t & operator = ( inout_ptr && rhs ) noexcept ; 1 Effects: moves each element of
intorhs , then sets* this torhs . p so that the destructor’s effects do not apply.nullptr 2 Equivalent to:
inout_ptr_t & operator = ( inout_ptr_t && rhs ) noexcept { s = std :: move ( rhs . s ); a = std :: move ( rhs . a ); p = std :: move ( rhs . p ); rhs . p = nullptr ; return * this ; } 19.11.11.3 Destructors [inout_ptr.class.dtor]
~ inout_ptr_t (); 1 Let
beSP (19.10.2).POINTER_OF ( Smart ) 2 Effects: reset the pointer stored in s, if p is not null using the
value stored inArgs , if any.* this 3 Equivalent to:
- —
ifif ( p != nullptr ) { s . reset ( static_cast < SP > ( p ), std :: forward < Args > ( args )... ); } is a valid member function onreset ,Smart - — otherwise
;if ( p != nullptr ) { s = Smart ( static_cast < SP > ( p ), std :: forward < Args > ( args )... ); } 19.11.11.4 Conversions [inout_ptr.class.conv]
operator Pointer * () noexcept ; operator void ** () noexcept ; // if Pointer not void* 1 Constraints: The second conversion shall participate in conversion if
is not of typePointer .void * 2 Effects: The first conversion returns a pointer to
. The second conversion returnp static_cast < void **> ( static_cast < void *> ( static_cast < Pointer *> ( * this ))); 
Insert §19.11.12 [inout_ptr]:
19.11.12 Function Template
[inout_ptr]inout_ptr 1 inout_ptr is a function template that produces an object of type inout_ptr_t (19.11.11).
namespace std { template < class Pointer , class Smart , class ... Args > auto inout_ptr ( Smart & s , Args && ... args ) noexcept -> inout_ptr_t < Smart , Pointer , Args ... > ; template < class Smart , class ... Args > auto inout_ptr ( Smart & s , Args && ... args ) noexcept -> decltype ( inout_ptr < Pointer > ( s , std :: forward < Args > ( args )...)); } 2 Effects: For the second overload, let
bePointer (19.10.2).POINTER_OF ( Smart ) 3 Equivalent to:
return inout_ptr_t < Smart , Pointer , Args ... > ( s , std :: forward < Args > ( args )...); 
8. Acknowledgements
Thank you to Lounge<C++>'s Cicada, melak47, rmf, and Puppy for reporting their initial experiences with such an abstraction nearly 5 years ago and helping JeanHeyd Meneide implement the first version of this.
Thank you to Mark Zeren for help in this investigation and analysis of the performance of smart pointers.