This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of CD1 status.
Section: 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 T*
pointer.
-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 T*
pointer. 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 rvalue D
mustshall not throw an exception.U*unique_ptr<U,E>::pointermustshall be implicitly convertible toT*pointer.
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. Dmustshall 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 of Dmustshall not throw an exception. Dmustshall not be a reference type or pointer type (diagnostic required).