unique_ptr
updateSection: 20.3.1 [unique.ptr] Status: CD1 Submitter: Howard Hinnant Opened: 2007-05-04 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [unique.ptr].
View all issues with CD1 status.
Discussion:
Since the publication of
N1856
there have been a few small but significant advances which should be included into
unique_ptr
. There exists a
example implementation
for all of these changes.
Even though unique_ptr<void>
is not a valid use case (unlike for shared_ptr<void>
),
unexpected cases to crop up which require the instantiation of the interface of unique_ptr<void>
even if it is never used. For example see 541 for how this accidently happened to auto_ptr
.
I believe the most robust way to protect unique_ptr
against this
type of failure is to augment the return type of unique_ptr<T>:operator*()
with
add_lvalue_reference<T>::type
. This means that given an instantiated unique_ptr<void>
the act of dereferencing it will simply return void
instead of causing a compile time failure.
This is simpler than creating a unique_ptr<void>
specialization which isn't robust in the
face of cv-
qualified void
types.
This resolution also supports instantiations such as unique_ptr<void, free_deleter>
which could be very useful to the client.
Efforts have been made to better support containers and smart pointers in shared
memory contexts. One of the key hurdles in such support is not assuming that a
pointer type is actually a T*
. This can easily be accomplished
for unique_ptr
by having the deleter define the pointer type:
D::pointer
. Furthermore this type can easily be defaulted to
T*
should the deleter D
choose not to define a pointer
type (example implementation
here).
This change has no run time overhead. It has no interface overhead on
authors of custom delter types. It simply allows (but not requires)
authors of custom deleter types to define a smart pointer for the
storage type of unique_ptr
if they find such functionality
useful. std::default_delete
is an example of a deleter which
defaults pointer
to T*
by simply ignoring this issue
and not including a pointer typedef
.
When the deleter type is a function pointer then it is unsafe to construct
a unique_ptr
without specifying the function pointer in the constructor.
This case is easy to check for with a static_assert
assuring that the
deleter is not a pointer type in those constructors which do not accept deleters.
unique_ptr<A, void(*)(void*)> p(new A); // error, no function given to delete the pointer!
[ Kona (2007): We don't like the solution given to the first bullet in light of concepts. The second bullet solves the problem of supporting fancy pointers for one library component only. The full LWG needs to decide whether to solve the problem of supporting fancy pointers piecemeal, or whether a paper addressing the whole library is needed. We think that the third bullet is correct. ]
[ Post Kona: Howard adds example user code related to the first bullet: ]
void legacy_code(void*, std::size_t); void foo(std::size_t N) { std::unique_ptr<void, void(*)(void*)> ptr(std::malloc(N), std::free); legacy_code(ptr.get(), N); } // unique_ptr used for exception safety purposes
I.e. unique_ptr<void>
is a useful tool that we don't want
to disable with concepts. The only part of unique_ptr<void>
we
want to disable (with concepts or by other means) are the two member functions:
T& operator*() const; T* operator->() const;
Proposed resolution:
[ I am grateful for the generous aid of Peter Dimov and Ion Gaztañaga in helping formulate and review the proposed resolutions below. ]
Change 20.3.1.3 [unique.ptr.single]:
template <class T, class D = default_delete<T>> class unique_ptr { ...T&typename add_lvalue_reference<T>::type operator*() const; ... };
Change 20.3.1.3.5 [unique.ptr.single.observers]:
T&typename add_lvalue_reference<T>::type operator*() const;
Change 20.3.1.3 [unique.ptr.single]:
template <class T, class D = default_delete<T>> class unique_ptr { public: typedef implementation (see description below) pointer; ... explicit unique_ptr(T*pointer p); ... unique_ptr(T*pointer p, implementation defined (see description below) d); unique_ptr(T*pointer p, implementation defined (see description below) d); ...T*pointer operator->() const;T*pointer get() const; ...T*pointer release(); void reset(T*pointer p =0pointer()); };
-3- If the type remove_reference<D>::type::pointer
exists, then unique_ptr<T, D>::pointer
is a typedef to
remove_reference<D>::type::pointer
. Otherwise
unique_ptr<T, D>::pointer
is a typedef to T*
.
The type unique_ptr<T, D>::pointer
shall be CopyConstructible
and CopyAssignable
.
Change 20.3.1.3.2 [unique.ptr.single.ctor]:
unique_ptr(T*pointer p); ... unique_ptr(T*pointer p, implementation defined d); unique_ptr(T*pointer p, implementation defined d); ... unique_ptr(T*pointer p, const A& d); unique_ptr(T*pointer p, A&& d); ... unique_ptr(T*pointer p, A& d); unique_ptr(T*pointer p, A&& d); ... unique_ptr(T*pointer p, const A& d); unique_ptr(T*pointer p, const A&& d); ...
-23- Requires: If D
is not a reference type,
construction of the deleter D
from an rvalue of type E
must shall be well formed and not throw an exception. If D
is a
reference type, then E
must shall be the same type as D
(diagnostic required). U*
unique_ptr<U,E>::pointer
must shall be implicitly convertible to
pointer.
T*
-25- Postconditions: get() == value u.get()
had before
the construction, modulo any required offset adjustments resulting from
the cast from U*
unique_ptr<U,E>::pointer
to
pointer. T*
get_deleter()
returns a reference to the
internally stored deleter which was constructed from
u.get_deleter()
.
Change 20.3.1.3.4 [unique.ptr.single.asgn]:
-8- Requires: Assignment of the deleter
D
from an rvalueD
mustshall not throw an exception.U*
unique_ptr<U,E>::pointer
mustshall be implicitly convertible topointer.T*
Change 20.3.1.3.5 [unique.ptr.single.observers]:
T*pointer operator->() const;...
T*pointer get() const;
Change 20.3.1.3.6 [unique.ptr.single.modifiers]:
T*pointer release();...
void reset(T*pointer p =0pointer());
Change 20.3.1.4 [unique.ptr.runtime]:
template <class T, class D> class unique_ptr<T[], D> { public: typedef implementation pointer; ... explicit unique_ptr(T*pointer p); ... unique_ptr(T*pointer p, implementation defined d); unique_ptr(T*pointer p, implementation defined d); ...T*pointer get() const; ...T*pointer release(); void reset(T*pointer p =0pointer()); };
Change 20.3.1.4.2 [unique.ptr.runtime.ctor]:
unique_ptr(T*pointer p); unique_ptr(T*pointer p, implementation defined d); unique_ptr(T*pointer p, implementation defined d);These constructors behave the same as in the primary template except that they do not accept pointer types which are convertible to
T*
pointer
. [Note: One implementation technique is to create private templated overloads of these members. -- end note]
Change 20.3.1.4.5 [unique.ptr.runtime.modifiers]:
void reset(T*pointer p =0pointer());-1- Requires: Does not accept pointer types which are convertible to
T*
pointer
(diagnostic required). [Note: One implementation technique is to create a private templated overload. -- end note]
Change 20.3.1.3.2 [unique.ptr.single.ctor]:
unique_ptr();Requires:
D
mustshall be default constructible, and that constructionmustshall not throw an exception.D
mustshall not be a reference type or pointer type (diagnostic required).unique_ptr(T*pointer p);Requires: The expression
D()(p)
mustshall be well formed. The default constructor ofD
mustshall not throw an exception.D
mustshall not be a reference type or pointer type (diagnostic required).