The C++ standard offers an aliasing constructor for shared_ptr
:
template <typename T>
class shared_ptr
{
template <typename U>
shared_ptr(const shared_ptr<U>& r, element_type* ptr);
};
Such constructor means that a shared_ptr
object can be created in a way that it owns one object, same as owned by r
, while at the same time storing a pointer to another object -- ptr
.
Also, the standard explicitly allows using this constructor to create a shared_ptr
which doesn't own anything (thus meeting the definition of 'empty'), but still points to some object (thus qualified as 'non-null'):
[util.smartptr.shared.const]:
17. [Note 2: This constructor allows creation of an empty shared_ptr instance with a non-null stored pointer. — end note]
Construction of an empty but non-null shared_ptr
could have been easily forbidden, for example by demanding that attempted construction of an empty shared_ptr
instance with a non-null stored pointer should throw an exception.
But the standard chose to allow it. I wonder why and what for.
What is the intended and canonical use scenario for an empty but non-null shared_ptr
?
The aliasing constructor was added to std::shared_ptr
during standardization, in N2351 "Improving shared_ptr for C++0x, Revision 2":
This feature extends the interface of
shared_ptr
in a backward-compatible way that increases its expressive power and is therefore strongly recommended to be added to the C++0x standard. It introduces no source- and binary compatibility issues.
At this time it was already noted that:
[Note: This constructor allows creation of an empty
shared_ptr
instance with a non-NULL stored pointer. --end note.]
The aliasing constructor was at the same time added to boost::shared_ptr
, which acted as a testbed: https://github.com/boostorg/smart_ptr/commit/54e12d03fdfec63b4d8ff41991c4e64af6b1b4b4 https://github.com/boostorg/smart_ptr/commit/ce72827dc73ac652ed07002b75f32e0171119c09
As for why this is permitted: there is a clear alternative, which is for the user to construct a shared_ptr
from an existing pointer and a no-op deleter:
auto sp = shared_ptr(p, [](auto){})
However, this is less efficient than aliasing the empty state (since a separate control block must be allocated) and less expressive, since it is not possible to determine that the deleter is a no-op. So there is little reason to forbid the former.