Title
Disallow unique_ptr<T&, D>
Status
voting
Section
[unique.ptr.single.general]
Submitter
Jonathan Wakely

Created on 2024-08-30.00:00:00 last changed yesterday

Messages

Date: 2024-11-13.20:38:33

Proposed resolution:

This wording is relative to N4988.

  1. Modify [unique.ptr.single.general] as indicated:

    -?- A program that instantiates the definition of unique_ptr<T, D> is ill-formed if `T*` is an invalid type.
    [Note: This prevents the intantiation of specializations such as unique_ptr<T&, D> and unique_ptr<int() const, D>. — end note]

    -1- The default type for the template parameter `D` is `default_delete`. A client-supplied template argument `D` shall be a function object type ([function.objects]), lvalue reference to function, or lvalue reference to function object type for which, given a value `d` of type `D` and a value `ptr` of type unique_ptr<T, D>::pointer, the expression `d(ptr)` is valid and has the effect of disposing of the pointer as appropriate for that deleter.

    -2- If the deleter’s type `D` is not a reference type, `D` shall meet the Cpp17Destructible requirements (Table 35).

    -3- If the qualified-id remove_reference_t<D>::pointer is valid and denotes a type ([temp.deduct]), then unique_ptr<T, D>::pointer shall be a synonym for remove_reference_t<D>::pointer. Otherwise unique_ptr<T, D>::pointer shall be a synonym for element_type*. The type unique_ptr<T, D>::pointer shall meet the Cpp17NullablePointer requirements (Table 36).

    -4- [Example 1:  Given an allocator type `X` ([allocator.requirements.general]) and letting `A` be a synonym for allocator_traits<X>, the types `A::pointer`, `A::const_pointer`, `A::void_pointer`, and `A::const_void_pointer` may be used as unique_ptr<T, D>::pointer. — end example]

Date: 2024-11-15.00:00:00

[ 2024-11-13; Reflector poll ]

Set status to Tentatively Ready after eight votes in favour during reflector poll.

Date: 2024-08-30.00:00:00

It seems that we currently allow nonsensical specializations of `unique_ptr` such as unique_ptr<int&, D> and unique_ptr<void()const, D> (a custom deleter that defines `D::pointer` is needed, because otherwise the `pointer` type would default to invalid types like int&* or void(*)()const). There seems to be no reason to support these "unique pointer to reference" and "unique pointer to abominable function type" specializations, or any specialization for a type that you couldn't form a raw pointer to.

Prior to C++17, the major library implementations rejected such specializations as a side effect of the constraints for the unique_ptr(auto_ptr<U>&&) constructor being defined in terms of is_convertible<U*, T*>. This meant that overload resolution for any constructor of `unique_ptr` would attempt to form the type `T*` and fail if that was invalid. With the removal of `auto_ptr` in C++17, that constructor was removed and now unique_ptr<int&, D> can be instantiated (assuming any zombie definition of `auto_ptr` is not enabled by the library). This wasn't intentional, but just an accident caused by not explicitly forbidding such types.

Discussion on the LWG reflector led to near-unanimous support for explicitly disallowing these specializations for non-pointable types.

History
Date User Action Args
2024-11-19 16:09:07adminsetstatus: ready -> voting
2024-11-13 20:38:33adminsetmessages: + msg14450
2024-11-13 20:38:33adminsetstatus: new -> ready
2024-08-31 14:11:41adminsetmessages: + msg14348
2024-08-30 00:00:00admincreate