After some years doing Rust and OCaml, coming back to C++ has made me realize that it lacks yet another smart pointer type, which would arguably be more general and less surprising than all the ones currently existing in the C++20 standard.
Inspired by Box<T>
in Rust, the std::box<T>
would be a heap-allocated smart pointer. The encapsulated object would be stored in dynamic memory (heap), with std::box<T>
serving as an RAII wrapper managing the object's lifecycle. Upon going out of scope, std::box<T>
would automatically delete the object it points to, thus preventing memory leaks.
However, it would fully embrace the essential C++ paradigm of value semantic (I know, value and pointer together sound weird): copying the pointer would copy the underling object, instead of sharing it like a shared_ptr
or a raw pointer. This is the reason why it's not named XXX_ptr
, it's a smart pointer which acts as a value.
Let's compare this hypothetical std::box<T>
with other pointer types in C++, providing a clear perspective of the distinct advantages it could offer over each.
raw value
is straightforward and efficient, it's the best data storage type when it's available (copyable, automatic garbage collection, value semantic). However, you can't allocate them dynamically and you can't build recursive data structure such as a linked list or a tree with them. In contrast,std::box<T>
would offer all these capabilities, making it a versatile choice for complex, dynamic data handling.raw pointer
provides the ability to create recursive data structures and are movable. However, they don't support automatic garbage collection and can lead to shallow copy issues. A shallow copy can be dangerous as it might lead to double deletion or dangling pointers.std::box<T>
addresses these issues by offering deep copying and automatic garbage collection, eliminating the chance of these common pitfalls.shared_ptr<T>
is a step forward from raw pointers as it includes reference counting and automatic deletion, making it safer. However, its default behavior of shallow copying can lead to unwanted side effects as multipleshared_ptr<T>
objects can point to the same underlying object.std::box<T>
could provide a safer alternative, performing deep copies by default and preserving object ownership semantics.unique_ptr<T>
is an excellent tool for expressing exclusive ownership, and it supports automatic deletion of the managed object. But it isn't copyable, which limits its usability in scenarios where copying an owned object is needed. However,std::box<T>
encompasses the best of bothshared_ptr<T>
andunique_ptr<T>
with the addition of being copyable (deep copy), making it potentially more flexible and easier to use.auto_ptr<T>
(deprecated in C++11) provides unique ownership semantics similar tostd::box<T>
, but it significantly differs in its copying behavior. When you copy anstd::auto_ptr
, it transfers ownership (like a move), leaving the originalstd::auto_ptr
in a null state. This unexpected behavior led to its deprecation (in C++, the default behavior is to copy, not move).std::box<T>
, with its deep copy functionality, could be seen as an enhanced and safer version ofstd::auto_ptr
.
Pointer Type | Value semantic | Movable | Copyable (Default) | Garbage Collection | Heap/Recursive |
---|---|---|---|---|---|
std::box |
Yes | Yes | Yes (Deep Copy) | Yes | Yes |
Raw Value | Yes | Yes | Yes (Deep Copy) | Yes | No |
Raw Pointer | No | No ownership | Yes (Shallow Copy) | No | Yes |
std::shared_ptr |
No | Yes | Yes (Shallow Copy) | Yes | Yes |
std::unique_ptr |
Yes | Yes | No | Yes | Yes |
std::auto_ptr |
Weird | Yes | Transfer Ownership | Yes | Yes |
This hypothetical std::box
is nothing new: it's ubiquitous in Rust, and its the way any data type works in OCaml (on the surface at least). One of the reason for preventing its mass adoption might be the performance implication : the key characteristic of std::box<T>
is its deep copy behavior which implies a full replication of the underlying object (it's actually similar to using a raw value, which also performs a deep copy operation when copied).
Let me know if any of this makes sense or if there is a better name for this (owned_ref<T>
? owned<T>
? ?)unique_ptr_but_you_can_copy_me_baby<T>