Title
unique_ptr<void>::operator* is not SFINAE-friendly
Status
new
Section
[unique.ptr.single.observers]
Submitter
Hewill Kang

Created on 2025-08-24.00:00:00 last changed 5 days ago

Messages

Date: 2025-08-26.17:06:01

Proposed resolution:

This wording is relative to N5014.

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

    constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));
    

    -?- Constraints: *declval<pointer>() is a well-formed expression.

    -1- Mandates: reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*declval<pointer>())> is `false`.

    -2- Preconditions: `get() != nullptr` is `true`.

    -3- Returns: `*get()`.

Date: 2025-08-15.00:00:00

[ 2025-08-26; Reflector discussion ]

During reflector triaging it had been pointed out that a better solution would be to constrain the `operator*` directly. The proposed wording has been updated to that effect.

Date: 2025-08-26.17:06:01

LWG 2762 added a conditional `noexcept` specification to `unique_ptr::operator*` to make it consistent with `shared_ptr::operator*`:

constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));

This unexpectedly makes unique_ptr<void>::operator* no longer SFINAE-friendly, for example:

#include <memory>

template<class T> concept dereferenceable = requires(T& t) { *t; };

static_assert( dereferenceable<int *>);
static_assert(!dereferenceable<void*>);

static_assert( dereferenceable<std::shared_ptr<int >>);
static_assert(!dereferenceable<std::shared_ptr<void>>);

static_assert( dereferenceable<std::unique_ptr<int >>);
static_assert( dereferenceable<std::unique_ptr<void>>); // hard error

Given that the standard intends for `operator*` of `shared_ptr` and `unique_ptr` to be SFINAE-friendly based on [util.smartptr.shared.obs], regardless of the value of `static_assert`, it is reasonable to assume that there should be no hard error here.

This wording is relative to N5014.

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

    constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>())see below);
    

    -1- Mandates: reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*declval<pointer>())> is `false`.

    -2- Preconditions: `get() != nullptr` is `true`.

    -3- Returns: `*get()`.

    -?- Remarks:: The exception specification is equivalent to:

    !requires { *declval<pointer>(); } || requires { { *declval<pointer>() } noexcept; }
    
History
Date User Action Args
2025-08-26 17:06:01adminsetmessages: + msg14965
2025-08-24 13:24:52adminsetmessages: + msg14954
2025-08-24 00:00:00admincreate