[PATCH] libstdc++: Make std::unique_ptr::operator* SFINAE-friendly

Nathan Myers ncm@cantrip.org
Thu Apr 30 22:55:01 GMT 2026


This seems to be saying that existing code performing the
check builds OK, but might be getting the wrong answer; and
that with this change we can ensure that, built to C++20 and
later, such code will still build and will furthermore reliably
get the right answer, while code built to an older Standard,
although it could still get the wrong answer, will anyway get
the same answer as before.

Do I understand correctly?


On 4/30/26 4:58 PM, Jonathan Wakely wrote:
> This implements LWG 4324, "unique_ptr<void>::operator* is not
> SFINAE-friendly", approved in Croydon, 2026.
> 
> The noexcept-specifier added to C++23 by LWG 2762 is ill-formed if the
> pointer type cannot be dereferenced, which means that code which was
> checking whether the function exists (e.g. in a SFINAE context) no
> longer works. Such code was always questionable, because the function
> body was ill-formed if the pointer isn't dereferenceable, so the SFINAE
> check was probably giving the wrong answer.
> 
> LWG 4324 adds a constraint to the function, so that it doesn't
> participate in overload resolution if it would be ill-formed. That's
> easy to implement for C++20 because we can just add a requires-clause.
> For C++11/14/17 we can't constrain it easily, so just adjust the
> noexcept-specifier so that it's not ill-formed.
> 
> libstdc++-v3/ChangeLog:
> 
> 	* include/bits/unique_ptr.h (unique_ptr::_Can_deref)
> 	(unique_ptr::_S_nothrow_deref): New helpers for pre-C++20.
> 	(operator*): Either constrain or use _S_nothrow_deref.
> 	* testsuite/20_util/unique_ptr/lwg4324.cc: New test.
> ---
> 
> Tested x86_64-linux.
> 
>   libstdc++-v3/include/bits/unique_ptr.h        | 42 ++++++++++++++++++-
>   .../testsuite/20_util/unique_ptr/lwg4324.cc   | 22 ++++++++++
>   2 files changed, 63 insertions(+), 1 deletion(-)
>   create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/lwg4324.cc
> 
> diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h
> index 5b7c8938ff74..222e61d221ed 100644
> --- a/libstdc++-v3/include/bits/unique_ptr.h
> +++ b/libstdc++-v3/include/bits/unique_ptr.h
> @@ -289,6 +289,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   	  __not_<is_array<_Up>>
>           >;
>   
> +#if ! __cpp_concepts
> +      template<typename _Ptr, typename = void>
> +	struct _Can_deref
> +	: false_type { };
> +
> +      template<typename _Ptr>
> +	struct _Can_deref<_Ptr, __void_t<decltype(*std::declval<_Ptr>())>>
> +	: true_type { };
> +
> +#if __cpp_constexpr >= 201304L // C++14 relaxed constexpr
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> +      template<typename _Ptr>
> +	static constexpr bool
> +	_S_nothrow_deref(int)
> +	{
> +	  if constexpr (_Can_deref<_Ptr>::value)
> +	    return noexcept(*std::declval<_Ptr>());
> +	  return false;
> +	}
> +#pragma GCC diagnostic pop
> +#else
> +      template<typename _Ptr, typename = decltype(*std::declval<_Ptr>())>
> +	static constexpr bool
> +	_S_nothrow_deref(int) { return noexcept(*std::declval<_Ptr>()); }
> +      template<typename _Ptr>
> +	static constexpr bool
> +	_S_nothrow_deref(...) { return false; }
> +#endif
> +#endif
> +
>       public:
>         // Constructors.
>   
> @@ -442,7 +473,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         /// Dereference the stored pointer.
>         _GLIBCXX23_CONSTEXPR
>         typename add_lvalue_reference<element_type>::type
> -      operator*() const noexcept(noexcept(*std::declval<pointer>()))
> +      operator*() const
> +      // _GLIBCXX_RESOLVE_LIB_DEFECTS
> +      // 2762. unique_ptr operator*() should be noexcept
> +      // 4324. unique_ptr<void>::operator* is not SFINAE-friendly
> +#if __cpp_concepts
> +      noexcept(noexcept(*std::declval<pointer>()))
> +      requires requires { *std::declval<pointer>(); }
> +#else
> +      noexcept(_S_nothrow_deref<pointer>(0))
> +#endif
>         {
>   #if _GLIBCXX_USE_BUILTIN_TRAIT(__reference_converts_from_temporary)
>   	// _GLIBCXX_RESOLVE_LIB_DEFECTS
> diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4324.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4324.cc
> new file mode 100644
> index 000000000000..13ebfb929fca
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4324.cc
> @@ -0,0 +1,22 @@
> +// { dg-do compile { target c++11 } }
> +
> +// LWG 4324. unique_ptr<void>::operator* is not SFINAE-friendly
> +
> +#include <memory>
> +
> +template<class T, bool = true>
> +struct nothrow_dereference
> +: std::false_type { };
> +
> +template<class T>
> +struct nothrow_dereference<T, noexcept(*std::declval<T>())>
> +: std::true_type { };
> +
> +static_assert( nothrow_dereference<std::unique_ptr<int>>::value, "");
> +static_assert( ! nothrow_dereference<std::unique_ptr<void>>::value, "");
> +
> +#if __cplusplus >= 202002L // C++20
> +template<class T> concept dereferenceable = requires(T& t) { *t; };
> +static_assert( dereferenceable<std::unique_ptr<int>>);
> +static_assert(!dereferenceable<std::unique_ptr<void>>);
> +#endif



More information about the Libstdc++ mailing list