Tested x86_64-linux. Pushe to trunk.

-- >8 --

This implements that changes from P1132R8, including optimized paths for
std::shared_ptr and std::unique_ptr.

For std::shared_ptr we pre-allocate a new control block in the
std::out_ptr_t constructor so that the destructor is non-throwing. This
requires some care because unlike the shared_ptr(Y*, D, A) constructor,
we don't want to invoke the deleter if allocating the control block
throws, because we don't own any pointer yet. In order to avoid the
unwanted deleter invocation, we create the control block manually. We
also want to avoid invoking the deleter on a null pointer on
destruction, so we destroy the control block manually if there is no
pointer to take ownership of.

For std::unique_ptr and for raw pointers, the out_ptr_t object hands out
direct access to the pointer, so that we don't have anything to do
(except possibly assign a new deleter) in the ~out_ptr_t destructor.

These optimizations avoid requiring additional temporary storage for the
pointer (and optional arguments), and avoid additional instructions to
copy that pointer into the smart pointer at the end.

libstdc++-v3/ChangeLog:

        PR libstdc++/111667
        * include/Makefile.am: Add new header.
        * include/Makefile.in: Regenerate.
        * include/bits/out_ptr.h: New file.
        * include/bits/shared_ptr.h (__is_shared_ptr): Move definition
        to here ...
        * include/bits/shared_ptr_atomic.h (__is_shared_ptr): ... from
        here.
        * include/bits/shared_ptr_base.h (__shared_count): Declare
        out_ptr_t as a friend.
        (_Sp_counted_deleter, __shared_ptr): Likewise.
        * include/bits/unique_ptr.h (unique_ptr, unique_ptr<T[], D>):
        Declare out_ptr_t and inout_ptr_t as friends.
        (__is_unique_ptr): Define new variable template.
        * include/bits/version.def (out_ptr): Define.
        * include/bits/version.h: Regenerate.
        * include/std/memory: Include new header.
        * testsuite/20_util/smartptr.adapt/inout_ptr/1.cc: New test.
        * testsuite/20_util/smartptr.adapt/inout_ptr/2.cc: New test.
        * testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc:
        New test.
        * testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc: New
        test.
        * testsuite/20_util/smartptr.adapt/out_ptr/1.cc: New test.
        * testsuite/20_util/smartptr.adapt/out_ptr/2.cc: New test.
        * testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc:
        New test.
        * testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc: New
        test.
---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/bits/out_ptr.h           | 432 ++++++++++++++++++
 libstdc++-v3/include/bits/shared_ptr.h        |   7 +
 libstdc++-v3/include/bits/shared_ptr_atomic.h |   5 -
 libstdc++-v3/include/bits/shared_ptr_base.h   |   9 +
 libstdc++-v3/include/bits/unique_ptr.h        |  21 +
 libstdc++-v3/include/bits/version.def         |   8 +
 libstdc++-v3/include/bits/version.h           |  23 +-
 libstdc++-v3/include/std/memory               |   5 +
 .../20_util/smartptr.adapt/inout_ptr/1.cc     |  47 ++
 .../20_util/smartptr.adapt/inout_ptr/2.cc     | 102 +++++
 .../inout_ptr/shared_ptr_neg.cc               |  11 +
 .../smartptr.adapt/inout_ptr/void_ptr.cc      |  35 ++
 .../20_util/smartptr.adapt/out_ptr/1.cc       | 116 +++++
 .../20_util/smartptr.adapt/out_ptr/2.cc       | 273 +++++++++++
 .../smartptr.adapt/out_ptr/shared_ptr_neg.cc  |   7 +
 .../smartptr.adapt/out_ptr/void_ptr.cc        |  60 +++
 18 files changed, 1152 insertions(+), 11 deletions(-)
 create mode 100644 libstdc++-v3/include/bits/out_ptr.h
 create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc
 create mode 100644 
libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc
 create mode 100644 
libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc
 create mode 100644 
libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc
 create mode 100644 
libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 6209f390e08..dab9f720cbb 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -130,6 +130,7 @@ bits_freestanding = \
        ${bits_srcdir}/max_size_type.h \
        ${bits_srcdir}/memoryfwd.h \
        ${bits_srcdir}/move.h \
+       ${bits_srcdir}/out_ptr.h \
        ${bits_srcdir}/predefined_ops.h \
        ${bits_srcdir}/parse_numbers.h \
        ${bits_srcdir}/ptr_traits.h \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 596fa0d2390..4f7ab2dfbab 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -485,6 +485,7 @@ bits_freestanding = \
        ${bits_srcdir}/max_size_type.h \
        ${bits_srcdir}/memoryfwd.h \
        ${bits_srcdir}/move.h \
+       ${bits_srcdir}/out_ptr.h \
        ${bits_srcdir}/predefined_ops.h \
        ${bits_srcdir}/parse_numbers.h \
        ${bits_srcdir}/ptr_traits.h \
diff --git a/libstdc++-v3/include/bits/out_ptr.h 
b/libstdc++-v3/include/bits/out_ptr.h
new file mode 100644
index 00000000000..49712fa7e31
--- /dev/null
+++ b/libstdc++-v3/include/bits/out_ptr.h
@@ -0,0 +1,432 @@
+// Smart pointer adaptors -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/bits/out_ptr.h
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly. @headername{memory}
+ */
+
+#ifndef _GLIBCXX_OUT_PTR_H
+#define _GLIBCXX_OUT_PTR_H 1
+
+#pragma GCC system_header
+
+#include <bits/version.h>
+
+#ifdef __glibcxx_out_ptr // C++ >= 23
+
+#include <tuple>
+#include <bits/ptr_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef __glibcxx_out_ptr // C++ >= 23
+  /// Adapt a smart pointer for functions taking an output pointer parameter.
+  template<typename _Smart, typename _Pointer, typename... _Args>
+    class out_ptr_t
+    {
+      static_assert(!__is_shared_ptr<_Smart> || sizeof...(_Args) != 0,
+                   "a deleter must be used when adapting std::shared_ptr "
+                   "with std::out_ptr");
+
+    public:
+      explicit
+      out_ptr_t(_Smart& __smart, _Args... __args)
+      : _M_impl{__smart, std::forward<_Args>(__args)...}
+      {
+       if constexpr (requires { _M_impl._M_out_init(); })
+         _M_impl._M_out_init();
+      }
+
+      out_ptr_t(const out_ptr_t&) = delete;
+
+      ~out_ptr_t() = default;
+
+      operator _Pointer*() const noexcept
+      { return _M_impl._M_get(); }
+
+      operator void**() const noexcept requires (!same_as<_Pointer, void*>)
+      {
+       static_assert(is_pointer_v<_Pointer>);
+       _Pointer* __p = *this;
+       return static_cast<void**>(static_cast<void*>(__p));
+      }
+
+    private:
+      // TODO: Move this to namespace scope? e.g. __detail::_Ptr_adapt_impl
+      template<typename, typename, typename...>
+       struct _Impl
+       {
+         // This constructor must not modify __s because out_ptr_t and
+         // inout_ptr_t want to do different things. After construction
+         // they call _M_out_init() or _M_inout_init() respectively.
+         _Impl(_Smart& __s, _Args&&... __args)
+         : _M_smart(__s), _M_args(std::forward<_Args>(__args)...)
+         { }
+
+         // Called by out_ptr_t to clear the smart pointer before using it.
+         void
+         _M_out_init()
+         {
+           // _GLIBCXX_RESOLVE_LIB_DEFECTS
+           // 3734. Inconsistency in inout_ptr and out_ptr for empty case
+           if constexpr (requires { _M_smart.reset(); })
+             _M_smart.reset();
+           else
+             _M_smart = _Smart();
+         }
+
+         // Called by inout_ptr_t to copy the smart pointer's value
+         // to the pointer that is returned from _M_get().
+         void
+         _M_inout_init()
+         { _M_ptr = _M_smart.release(); }
+
+         // The pointer value returned by operator Pointer*().
+         _Pointer*
+         _M_get() const
+         { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); }
+
+         // Finalize the effects on the smart pointer.
+         ~_Impl() noexcept(false);
+
+         _Smart& _M_smart;
+         [[no_unique_address]] _Pointer _M_ptr{};
+         [[no_unique_address]] tuple<_Args...> _M_args;
+       };
+
+      // Partial specialization for raw pointers.
+      template<typename _Tp>
+       struct _Impl<_Tp*, _Tp*>
+       {
+         void
+         _M_out_init()
+         { _M_p = nullptr; }
+
+         void
+         _M_inout_init()
+         { }
+
+         _Tp**
+         _M_get() const
+         { return __builtin_addressof(const_cast<_Tp*&>(_M_p)); }
+
+         _Tp*& _M_p;
+       };
+
+      // Partial specialization for raw pointers, with conversion.
+      template<typename _Tp, typename _Ptr> requires (!is_same_v<_Ptr, _Tp*>)
+       struct _Impl<_Tp*, _Ptr>
+       {
+         explicit
+         _Impl(_Tp*& __p)
+         : _M_p(__p)
+         { }
+
+         void
+         _M_out_init()
+         { _M_p = nullptr; }
+
+         void
+         _M_inout_init()
+         { _M_ptr = _M_p; }
+
+         _Pointer*
+         _M_get() const
+         { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); }
+
+         ~_Impl() { _M_p = static_cast<_Tp*>(_M_ptr); }
+
+         _Tp*& _M_p;
+         _Pointer _M_ptr{};
+       };
+
+      // Partial specialization for std::unique_ptr.
+      // This specialization gives direct access to the private member
+      // of the unique_ptr, avoiding the overhead of storing a separate
+      // pointer and then resetting the unique_ptr in the destructor.
+      // FIXME: constrain to only match the primary template,
+      // not program-defined specializations of unique_ptr.
+      template<typename _Tp, typename _Del>
+       struct _Impl<unique_ptr<_Tp, _Del>,
+                    typename unique_ptr<_Tp, _Del>::pointer>
+       {
+         void
+         _M_out_init()
+         { _M_smart.reset(); }
+
+         _Pointer*
+         _M_get() const noexcept
+         { return __builtin_addressof(_M_smart._M_t._M_ptr()); }
+
+         _Smart& _M_smart;
+       };
+
+      // Partial specialization for std::unique_ptr with replacement deleter.
+      // FIXME: constrain to only match the primary template,
+      // not program-defined specializations of unique_ptr.
+      template<typename _Tp, typename _Del, typename _Del2>
+       struct _Impl<unique_ptr<_Tp, _Del>,
+                    typename unique_ptr<_Tp, _Del>::pointer, _Del2>
+       {
+         void
+         _M_out_init()
+         { _M_smart.reset(); }
+
+         _Pointer*
+         _M_get() const noexcept
+         { return __builtin_addressof(_M_smart._M_t._M_ptr()); }
+
+         ~_Impl()
+         {
+           if (_M_smart.get())
+             _M_smart._M_t._M_deleter() = std::forward<_Del2>(_M_del);
+         }
+
+         _Smart& _M_smart;
+         [[no_unique_address]] _Del2 _M_del;
+       };
+
+      // Partial specialization for std::shared_ptr.
+      // This specialization gives direct access to the private member
+      // of the shared_ptr, avoiding the overhead of storing a separate
+      // pointer and then resetting the shared_ptr in the destructor.
+      // A new control block is allocated in the constructor, so that if
+      // allocation fails it doesn't throw an exception from the destructor.
+      template<typename _Tp, typename _Del, typename _Alloc>
+       requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>)
+       struct _Impl<shared_ptr<_Tp>,
+                    typename shared_ptr<_Tp>::element_type*, _Del, _Alloc>
+       {
+         _Impl(_Smart& __s, _Del __d, _Alloc __a = _Alloc())
+         : _M_smart(__s)
+         {
+           // We know shared_ptr cannot be used with inout_ptr_t
+           // so we can do all set up here, instead of in _M_out_init().
+           _M_smart.reset();
+
+           // Similar to the shared_ptr(Y*, D, A) constructor, except that if
+           // the allocation throws we do not need (or want) to call deleter.
+           typename _Scd::__allocator_type __a2(__a);
+           auto __mem = __a2.allocate(1);
+           ::new (__mem) _Scd(nullptr, std::forward<_Del>(__d),
+                              std::forward<_Alloc>(__a));
+           _M_smart._M_refcount._M_pi = __mem;
+         }
+
+         _Pointer*
+         _M_get() const noexcept
+         { return __builtin_addressof(_M_smart._M_ptr); }
+
+         ~_Impl()
+         {
+           auto& __pi = _M_smart._M_refcount._M_pi;
+
+           if (_Sp __ptr = _M_smart.get())
+             static_cast<_Scd*>(__pi)->_M_impl._M_ptr = __ptr;
+           else // Destroy the control block manually without invoking deleter.
+             std::__exchange(__pi, nullptr)->_M_destroy();
+         }
+
+         _Smart& _M_smart;
+
+         using _Sp = typename _Smart::element_type*;
+         using _Scd = _Sp_counted_deleter<_Sp, decay_t<_Del>,
+                                          remove_cvref_t<_Alloc>,
+                                          __default_lock_policy>;
+       };
+
+      // Partial specialization for std::shared_ptr, without custom allocator.
+      template<typename _Tp, typename _Del>
+       requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>)
+       struct _Impl<shared_ptr<_Tp>,
+                    typename shared_ptr<_Tp>::element_type*, _Del>
+       : _Impl<_Smart, _Pointer, _Del, allocator<void>>
+       {
+         using _Impl<_Smart, _Pointer, _Del, allocator<void>>::_Impl;
+       };
+
+      using _Impl_t = _Impl<_Smart, _Pointer, _Args...>;
+
+      _Impl_t _M_impl;
+
+      template<typename, typename, typename...> friend class inout_ptr_t;
+    };
+
+  /// Adapt a smart pointer for functions taking an output pointer parameter.
+  template<typename _Smart, typename _Pointer, typename... _Args>
+    class inout_ptr_t
+    {
+      static_assert(!__is_shared_ptr<_Smart>,
+                   "std::inout_ptr can not be used to wrap std::shared_ptr");
+
+    public:
+      explicit
+      inout_ptr_t(_Smart& __smart, _Args... __args)
+      : _M_impl{__smart, std::forward<_Args>(__args)...}
+      {
+       if constexpr (requires { _M_impl._M_inout_init(); })
+         _M_impl._M_inout_init();
+      }
+
+      inout_ptr_t(const inout_ptr_t&) = delete;
+
+      ~inout_ptr_t() = default;
+
+      operator _Pointer*() const noexcept
+      { return _M_impl._M_get(); }
+
+      operator void**() const noexcept requires (!same_as<_Pointer, void*>)
+      {
+       static_assert(is_pointer_v<_Pointer>);
+       _Pointer* __p = *this;
+       return static_cast<void**>(static_cast<void*>(__p));
+      }
+
+    private:
+      // Avoid an invalid instantiation of out_ptr_t<shared_ptr<T>, ...>
+      using _Out_ptr_t
+       = __conditional_t<__is_shared_ptr<_Smart>,
+                         out_ptr_t<void*, void*>,
+                         out_ptr_t<_Smart, _Pointer, _Args...>>;
+      using _Impl_t = typename _Out_ptr_t::_Impl_t;
+      _Impl_t _M_impl;
+    };
+
+/// @cond undocumented
+namespace __detail
+{
+  // POINTER_OF metafunction
+  template<typename _Tp>
+    consteval auto
+    __pointer_of()
+    {
+      if constexpr (requires { typename _Tp::pointer; })
+       return type_identity<typename _Tp::pointer>{};
+      else if constexpr (requires { typename _Tp::element_type; })
+       return type_identity<typename _Tp::element_type*>{};
+      else
+       {
+         using _Traits = pointer_traits<_Tp>;
+         if constexpr (requires { typename _Traits::element_type; })
+           return type_identity<typename _Traits::element_type*>{};
+       }
+      // else POINTER_OF(S) is not a valid type, return void.
+    }
+
+  // POINTER_OF_OR metafunction
+  template<typename _Smart, typename _Ptr>
+    consteval auto
+    __pointer_of_or()
+    {
+      using _TypeId = decltype(__detail::__pointer_of<_Smart>());
+      if constexpr (is_void_v<_TypeId>)
+       return type_identity<_Ptr>{};
+      else
+       return _TypeId{};
+    }
+
+  // Returns Pointer if !is_void_v<Pointer>, otherwise POINTER_OF(Smart).
+  template<typename _Ptr, typename _Smart>
+    consteval auto
+    __choose_ptr()
+    {
+      if constexpr (!is_void_v<_Ptr>)
+       return type_identity<_Ptr>{};
+      else
+       return __detail::__pointer_of<_Smart>();
+    }
+
+  template<typename _Smart, typename _Sp, typename... _Args>
+    concept __resettable = requires (_Smart& __s) {
+      __s.reset(std::declval<_Sp>(), std::declval<_Args>()...);
+    };
+}
+/// @endcond
+
+  template<typename _Pointer = void, typename _Smart, typename... _Args>
+    inline auto
+    out_ptr(_Smart& __s, _Args&&... __args)
+    {
+      using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>());
+      static_assert(!is_void_v<_TypeId>, "first argument to std::out_ptr "
+                   "must be a pointer-like type");
+
+      using _Ret = out_ptr_t<_Smart, typename _TypeId::type, _Args&&...>;
+      return _Ret(__s, std::forward<_Args>(__args)...);
+    }
+
+  template<typename _Pointer = void, typename _Smart, typename... _Args>
+    inline auto
+    inout_ptr(_Smart& __s, _Args&&... __args)
+    {
+      using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>());
+      static_assert(!is_void_v<_TypeId>, "first argument to std::inout_ptr "
+                   "must be a pointer-like type");
+
+      using _Ret = inout_ptr_t<_Smart, typename _TypeId::type, _Args&&...>;
+      return _Ret(__s, std::forward<_Args>(__args)...);
+    }
+
+  template<typename _Smart, typename _Pointer, typename... _Args>
+  template<typename _Smart2, typename _Pointer2, typename... _Args2>
+    inline
+    out_ptr_t<_Smart, _Pointer, _Args...>::
+    _Impl<_Smart2, _Pointer2, _Args2...>::~_Impl()
+    {
+      using _TypeId = decltype(__detail::__pointer_of_or<_Smart, _Pointer>());
+      using _Sp = typename _TypeId::type;
+
+      if (!_M_ptr)
+       return;
+
+      _Smart& __s = _M_smart;
+      _Pointer& __p = _M_ptr;
+
+      auto __reset = [&](auto&&... __args) {
+       if constexpr (__detail::__resettable<_Smart, _Sp, _Args...>)
+         __s.reset(static_cast<_Sp>(__p), std::forward<_Args>(__args)...);
+       else if constexpr (is_constructible_v<_Smart, _Sp, _Args...>)
+         __s = _Smart(static_cast<_Sp>(__p), std::forward<_Args>(__args)...);
+       else
+         static_assert(is_constructible_v<_Smart, _Sp, _Args...>);
+      };
+
+      if constexpr (sizeof...(_Args) >= 2)
+       std::apply(__reset, std::move(_M_args));
+      else if constexpr (sizeof...(_Args) == 1)
+       __reset(std::get<0>(std::move(_M_args)));
+      else
+       __reset();
+    }
+#endif // __glibcxx_out_ptr
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+
+#endif // __glibcxx_out_ptr
+#endif /* _GLIBCXX_OUT_PTR_H */
+
diff --git a/libstdc++-v3/include/bits/shared_ptr.h 
b/libstdc++-v3/include/bits/shared_ptr.h
index 02bfdabfd1c..e3c48dead72 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -1158,6 +1158,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     };
 
+#if __cpp_variable_templates
+  template<typename _Tp>
+    static constexpr bool __is_shared_ptr = false;
+  template<typename _Tp>
+    static constexpr bool __is_shared_ptr<shared_ptr<_Tp>> = true;
+#endif
+
   /// @} relates shared_ptr
   /// @} group pointer_abstractions
 
diff --git a/libstdc++-v3/include/bits/shared_ptr_atomic.h 
b/libstdc++-v3/include/bits/shared_ptr_atomic.h
index d0be43aec2d..22b1a1d6c50 100644
--- a/libstdc++-v3/include/bits/shared_ptr_atomic.h
+++ b/libstdc++-v3/include/bits/shared_ptr_atomic.h
@@ -388,11 +388,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @{
    */
 
-  template<typename _Up>
-    static constexpr bool __is_shared_ptr = false;
-  template<typename _Up>
-    static constexpr bool __is_shared_ptr<shared_ptr<_Up>> = true;
-
   template<typename _Tp>
     class _Sp_atomic
     {
diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h 
b/libstdc++-v3/include/bits/shared_ptr_base.h
index edb3eb654ea..1961a9ae9e9 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -549,6 +549,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
     private:
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+#endif
       _Impl _M_impl;
     };
 
@@ -1126,6 +1129,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #ifdef __glibcxx_atomic_shared_ptr
       template<typename> friend class _Sp_atomic;
 #endif
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+#endif
 
       _Sp_counted_base<_Lp>*  _M_pi;
     };
@@ -1776,6 +1782,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #ifdef __glibcxx_atomic_shared_ptr
       friend _Sp_atomic<shared_ptr<_Tp>>;
 #endif
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+#endif
 
       element_type*       _M_ptr;         // Contained pointer.
       __shared_count<_Lp>  _M_refcount;    // Reference counter.
diff --git a/libstdc++-v3/include/bits/unique_ptr.h 
b/libstdc++-v3/include/bits/unique_ptr.h
index 164f5f7e945..9d6f5af0a04 100644
--- a/libstdc++-v3/include/bits/unique_ptr.h
+++ b/libstdc++-v3/include/bits/unique_ptr.h
@@ -515,6 +515,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Disable copy from lvalue.
       unique_ptr(const unique_ptr&) = delete;
       unique_ptr& operator=(const unique_ptr&) = delete;
+
+    private:
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...>
+       friend class out_ptr_t;
+      template<typename, typename, typename...>
+       friend class inout_ptr_t;
+#endif
   };
 
   // 20.7.1.3 unique_ptr for array objects with a runtime length
@@ -789,6 +797,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Disable copy from lvalue.
       unique_ptr(const unique_ptr&) = delete;
       unique_ptr& operator=(const unique_ptr&) = delete;
+
+    private:
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+      template<typename, typename, typename...> friend class inout_ptr_t;
+#endif
     };
 
   /// @{
@@ -1140,6 +1154,13 @@ namespace __detail
     }
 #endif // C++20 && HOSTED
 
+#if __cpp_variable_templates
+  template<typename _Tp>
+    static constexpr bool __is_unique_ptr = false;
+  template<typename _Tp, typename _Del>
+    static constexpr bool __is_unique_ptr<unique_ptr<_Tp, _Del>> = true;
+#endif
+
   /// @} group pointer_abstractions
 
 #if __cplusplus >= 201703L
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 2aab9426ecc..60400b3b8d2 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1568,6 +1568,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = out_ptr;
+  values = {
+    v = 202106;
+    cxxmin = 23;
+  };
+};
+
 ftms = {
   name = spanstream;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 6829c70cf48..2e2af3e0c41 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1923,6 +1923,17 @@
 #undef __glibcxx_want_move_only_function
 
 // from version.def line 1572
+#if !defined(__cpp_lib_out_ptr)
+# if (__cplusplus >= 202100L)
+#  define __glibcxx_out_ptr 202106L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_out_ptr)
+#   define __cpp_lib_out_ptr 202106L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_out_ptr) && defined(__glibcxx_want_out_ptr) */
+#undef __glibcxx_want_out_ptr
+
+// from version.def line 1580
 #if !defined(__cpp_lib_spanstream)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__glibcxx_span)
 #  define __glibcxx_spanstream 202106L
@@ -1933,7 +1944,7 @@
 #endif /* !defined(__cpp_lib_spanstream) && defined(__glibcxx_want_spanstream) 
*/
 #undef __glibcxx_want_spanstream
 
-// from version.def line 1582
+// from version.def line 1590
 #if !defined(__cpp_lib_stacktrace)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (_GLIBCXX_HAVE_STACKTRACE)
 #  define __glibcxx_stacktrace 202011L
@@ -1944,7 +1955,7 @@
 #endif /* !defined(__cpp_lib_stacktrace) && defined(__glibcxx_want_stacktrace) 
*/
 #undef __glibcxx_want_stacktrace
 
-// from version.def line 1592
+// from version.def line 1600
 #if !defined(__cpp_lib_string_contains)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
 #  define __glibcxx_string_contains 202011L
@@ -1955,7 +1966,7 @@
 #endif /* !defined(__cpp_lib_string_contains) && 
defined(__glibcxx_want_string_contains) */
 #undef __glibcxx_want_string_contains
 
-// from version.def line 1601
+// from version.def line 1609
 #if !defined(__cpp_lib_string_resize_and_overwrite)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
 #  define __glibcxx_string_resize_and_overwrite 202110L
@@ -1966,7 +1977,7 @@
 #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && 
defined(__glibcxx_want_string_resize_and_overwrite) */
 #undef __glibcxx_want_string_resize_and_overwrite
 
-// from version.def line 1610
+// from version.def line 1618
 #if !defined(__cpp_lib_fstream_native_handle)
 # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
 #  define __glibcxx_fstream_native_handle 202306L
@@ -1977,7 +1988,7 @@
 #endif /* !defined(__cpp_lib_fstream_native_handle) && 
defined(__glibcxx_want_fstream_native_handle) */
 #undef __glibcxx_want_fstream_native_handle
 
-// from version.def line 1619
+// from version.def line 1627
 #if !defined(__cpp_lib_ratio)
 # if (__cplusplus >  202302L)
 #  define __glibcxx_ratio 202306L
@@ -1988,7 +1999,7 @@
 #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
 #undef __glibcxx_want_ratio
 
-// from version.def line 1627
+// from version.def line 1635
 #if !defined(__cpp_lib_to_string)
 # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars)
 #  define __glibcxx_to_string 202306L
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index ac83761a26c..a526173b8cc 100644
--- a/libstdc++-v3/include/std/memory
+++ b/libstdc++-v3/include/std/memory
@@ -91,6 +91,10 @@
 #  include <bits/uses_allocator_args.h>
 #endif
 
+#if __cplusplus > 202002L
+#  include <bits/out_ptr.h>
+#endif
+
 #define __glibcxx_want_allocator_traits_is_always_equal
 #define __glibcxx_want_assume_aligned
 #define __glibcxx_want_atomic_shared_ptr
@@ -99,6 +103,7 @@
 #define __glibcxx_want_constexpr_memory
 #define __glibcxx_want_enable_shared_from_this
 #define __glibcxx_want_make_unique
+#define __glibcxx_want_out_ptr
 #define __glibcxx_want_parallel_algorithm
 #define __glibcxx_want_ranges
 #define __glibcxx_want_raw_memory_algorithms
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc
new file mode 100644
index 00000000000..c42e5e13b75
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc
@@ -0,0 +1,47 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+// C++23 [inout.ptr.t] Class template inout_ptr_t
+
+struct star_fish* star_fish_alloc();
+int star_fish_populate(struct star_fish** ps, const char* description);
+
+struct star_fish_deleter {
+  void operator() (struct star_fish* c) const noexcept;
+};
+
+using star_fish_ptr = std::unique_ptr<star_fish, star_fish_deleter>;
+
+// Example 1 from [inout.ptr.t]
+int main(int, char**)
+{
+  star_fish_ptr peach(star_fish_alloc());
+  // ...
+  // used, need to re-make
+  int err = star_fish_populate(std::inout_ptr(peach), "caring clown-fish 
liker");
+  return 0;
+}
+
+#include <cstdint>
+
+star_fish* star_fish_alloc()
+{
+  static std::uintptr_t counter = 1;
+  return reinterpret_cast<star_fish*>(counter++);
+}
+
+void star_fish_deleter::operator()(star_fish* c) const noexcept
+{
+  static std::uintptr_t counter = 1;
+  VERIFY(reinterpret_cast<std::uintptr_t>(c) == counter++);
+}
+
+int star_fish_populate(star_fish** ps, const char*)
+{
+  VERIFY(ps);
+  star_fish_deleter()(*ps);
+  *ps = star_fish_alloc();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc
new file mode 100644
index 00000000000..ca6076209c2
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc
@@ -0,0 +1,102 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+// C++23 [inout.ptr.t] Class template inout_ptr_t
+
+int counter = 0;
+
+void
+test_unique_ptr()
+{
+  int* ip = new int(1);
+  std::unique_ptr<int> up(ip);
+  {
+    auto iop = std::inout_ptr(up);
+    int** ipp = iop;
+    VERIFY( *ipp == ip );
+    delete *ipp;
+    *ipp = new int(2);
+  }
+  VERIFY( *up == 2 );
+
+  ip = up.get();
+  {
+    std::default_delete<int> d;
+    auto iop = std::inout_ptr(up, d);
+    int** ipp = iop;
+    VERIFY( *ipp == ip );
+    delete *ipp;
+    *ipp = new int(3);
+  }
+  VERIFY( *up == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  std::unique_ptr<long, D> upd(new long(3), D(11));
+  {
+    auto iop = std::inout_ptr(upd);
+    VERIFY( counter == 0 );
+    long** lpp = iop;
+    VERIFY( **lpp == 3 );
+    delete *lpp;
+    *lpp = new long(4);
+  }
+  VERIFY( *upd == 4 );
+  VERIFY( upd.get_deleter().id == 11 );
+  VERIFY( counter == 0 );
+
+  {
+    D d(22);
+    auto iop = std::inout_ptr(upd, d);
+    VERIFY( counter == 0 );
+    long** lpp = iop;
+    VERIFY( **lpp == 4 );
+    delete *lpp;
+    *lpp = nullptr;
+  }
+  VERIFY( upd == nullptr );
+  VERIFY( upd.get_deleter().id == 11 ); // Deleter not replaced if p is null.
+  VERIFY( counter == 0 );
+
+  upd.reset(new long(5));
+  {
+    D d(33);
+    auto iop = std::inout_ptr(upd, d);
+    VERIFY( counter == 0 );
+    long** lpp = iop;
+    VERIFY( **lpp == 5 );
+    delete *lpp;
+    *lpp = new long(6);
+  }
+  VERIFY( *upd == 6 );
+  VERIFY( upd.get_deleter().id == 33 );
+  VERIFY( counter == 0 );
+
+  struct Base { };
+  struct Derived : Base
+  {
+    Derived(int id) : id(id) { }
+    int id;
+  };
+  std::unique_ptr<Derived> upbd(new Derived(1));
+  {
+    auto iop = std::inout_ptr<Base*>(upbd);
+    Base** bpp = iop;
+    VERIFY( static_cast<Derived*>(*bpp)->id == 1 );
+    delete (Derived*)*bpp;
+    *bpp = new Derived(2);
+  }
+  VERIFY( upbd->id == 2 );
+}
+
+int main()
+{
+  test_unique_ptr();
+}
diff --git 
a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc
new file mode 100644
index 00000000000..b4ecf936105
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+
+std::shared_ptr<int> sp;
+auto iop = std::inout_ptr(sp); // { dg-error "here" }
+std::shared_ptr<long> sp2;
+std::default_delete<int> d;
+auto iop2 = std::inout_ptr(sp2, d); // { dg-error "here" }
+
+// { dg-error "can not be used" "" { target *-*-* } 0 }
diff --git 
a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc
new file mode 100644
index 00000000000..4fce3df6b30
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc
@@ -0,0 +1,35 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+std::unique_ptr<int> intptr;
+
+void
+val_inout(void** p)
+{
+  // The smart pointer should have been released by the inout_ptr_t constructor
+  VERIFY( intptr == nullptr );
+  // The initial value of the pointer should be non-null.
+  VERIFY( *(int*)*p == 5678 );
+  // Although the implementation is allowed to access the unique_ptr's
+  // pointer directly, it can't do that here because the unique_ptr stores
+  // an int* and we are reading from and writing to a void*.
+  VERIFY( intptr.get() != *p );
+
+  // Return a heap-allocated int in *p.
+  *p = (void*) new int(999);
+}
+
+void
+test_inout_ptr_void()
+{
+  intptr.reset(new int(5678));
+  val_inout(std::inout_ptr<void*>(intptr));
+  VERIFY( *intptr == 999 );
+}
+
+int main()
+{
+  test_inout_ptr_void();
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc
new file mode 100644
index 00000000000..ed542a04fc9
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc
@@ -0,0 +1,116 @@
+// { dg-do run { target c++23 } }
+// { dg-require-fileio "" }
+// { dg-additional-files "thirty_years_among_the_dead_preproc.txt" }
+
+#include <memory>
+#include <cstdio>
+#include <testsuite_hooks.h>
+
+// C++23 [out.ptr.t] Class template out_ptr_t
+
+int fopen_s(std::FILE** f, const char* name, const char* mode);
+
+struct fclose_deleter {
+  void operator()(std::FILE* f) const noexcept {
+    std::fclose(f);
+  }
+};
+
+// Example 1 from [out.ptr.t]
+void
+test_example_1()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+  int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// Same again without explicit template argument list.
+void
+test_example_1_2()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// And again with a deleter argument.
+void
+test_example_1_3()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr, fclose_deleter()), file_name, 
"r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// Same again using std::shared_ptr
+void
+test_example_1_sp()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::shared_ptr<std::FILE> file_ptr;
+  int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr, fclose_deleter()),
+                   file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// And again without explicit template argument list.
+void
+test_example_1_sp_2()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::shared_ptr<std::FILE> file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr, fclose_deleter()), file_name, 
"r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// And again using a raw pointer.
+void
+test_example_1_raw()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::FILE* file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+  std::fclose(file_ptr);
+}
+
+int main()
+{
+  test_example_1();
+  test_example_1_2();
+  test_example_1_3();
+  test_example_1_sp();
+  test_example_1_sp_2();
+  test_example_1_raw();
+}
+
+#include <cerrno>
+
+int fopen_s(std::FILE** f, const char* name, const char* mode)
+{
+  if ((*f = std::fopen(name, mode)))
+    return 0;
+  return errno;
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc
new file mode 100644
index 00000000000..77f952238eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc
@@ -0,0 +1,273 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+void
+test_unique_ptr()
+{
+  std::unique_ptr<int> up(new int(1));
+  {
+    auto op = std::out_ptr(up);
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *up == 2 );
+
+  {
+    std::default_delete<int> d;
+    auto op = std::out_ptr(up, d);
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(3);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *up == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  std::unique_ptr<long, D> upd(new long(1), D(11));
+  {
+    auto op = std::out_ptr(upd);
+    VERIFY( counter == 1 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(4);
+
+    const auto& cop = op;
+    VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+  }
+  VERIFY( *upd == 4 );
+  VERIFY( upd.get_deleter().id == 11 );
+  VERIFY( counter == 1 );
+
+  {
+    D d(33);
+    auto op = std::out_ptr(upd, d);
+    VERIFY( counter == 2 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+
+    const auto& cop = op;
+    VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+  }
+  VERIFY( upd == nullptr );
+  VERIFY( upd.get_deleter().id == 11 ); // Deleter not replaced if p is null.
+  VERIFY( counter == 2 );
+
+  {
+    D d(33);
+    auto op = std::out_ptr(upd, d);
+    VERIFY( counter == 2 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(5);
+  }
+  VERIFY( *upd == 5 );
+  VERIFY( upd.get_deleter().id == 33 );
+  VERIFY( counter == 2 );
+
+  struct Base { };
+  struct Derived : Base
+  {
+    Derived(int id) : id(id) { }
+    int id;
+  };
+  std::unique_ptr<Derived> upbd(new Derived(1));
+  {
+    auto op = std::out_ptr<Base*>(upbd);
+    Base** bpp = op;
+    VERIFY( *bpp == nullptr );
+    *bpp = new Derived(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<Base**>(cop) == static_cast<Base**>(op) );
+  }
+  VERIFY( upbd->id == 2 );
+}
+
+void deleter_function(float* p) { delete p; }
+
+void
+test_shared_ptr()
+{
+  using DD = std::default_delete<int>;
+
+  std::shared_ptr<int> sp(new int(1));
+  {
+    auto op = std::out_ptr(sp, DD{});
+    VERIFY( sp == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *sp == 2 );
+
+  {
+    auto op = std::out_ptr(sp, DD{});
+    VERIFY( sp == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(3);
+  }
+  VERIFY( *sp == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  std::shared_ptr<long> spd(new long(1), D(11));
+  {
+    auto op = std::out_ptr(spd, D(22));
+    VERIFY( counter == 1 );
+    VERIFY( spd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(5);
+  }
+  VERIFY( counter == 1 );
+  VERIFY( *spd == 5 );
+  VERIFY( std::get_deleter<D>(spd)->id == 22 );
+
+  struct Base { };
+  struct Derived : Base
+  {
+    Derived(int id) : id(id) { }
+    int id;
+  };
+  std::shared_ptr<Derived> spbd(new Derived(1));
+  {
+    auto op = std::out_ptr<Base*>(spbd, std::default_delete<Derived>());
+    Base** bpp = op;
+    VERIFY( *bpp == nullptr );
+    *bpp = new Derived(2);
+  }
+  VERIFY( spbd->id == 2 );
+
+  std::shared_ptr<float> spf;
+  {
+    auto op = std::out_ptr(spf, deleter_function);
+    float** fpp = op;
+    *fpp = new float(0.5);
+  }
+  VERIFY( std::get_deleter<void(*)(float*)>(spf) != nullptr );
+  VERIFY( *std::get_deleter<void(*)(float*)>(spf) == &deleter_function );
+  VERIFY( *spf == 0.5 );
+}
+
+void
+test_custom_ptr()
+{
+  struct UPtr : std::unique_ptr<int>
+  {
+    using std::unique_ptr<int>::unique_ptr;
+  };
+
+  UPtr up(new int(1));
+  {
+    auto op = std::out_ptr(up);
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *up == 2 );
+
+  {
+    auto op = std::out_ptr(up, std::default_delete<int>{});
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(3);
+  }
+  VERIFY( *up == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  struct UDPtr : std::unique_ptr<long, D>
+  {
+    using std::unique_ptr<long, D>::unique_ptr;
+  };
+  UDPtr upd(new long(1), D(11));
+  {
+    auto op = std::out_ptr(upd);
+    VERIFY( counter == 1 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(4);
+  }
+  VERIFY( counter == 1 );
+  VERIFY( *upd == 4 );
+  VERIFY( upd.get_deleter().id == 11 );
+
+  {
+    auto op = std::out_ptr(upd, D(22));
+    VERIFY( upd == nullptr );
+    VERIFY( counter == 2 );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(5);
+  }
+  VERIFY( counter == 2 );
+  VERIFY( *upd == 5 );
+  VERIFY( upd.get_deleter().id == 22 );
+}
+
+void
+test_raw_ptr()
+{
+  long l = 5, l2 = 6;
+  long* lp = &l;
+  {
+    auto op = std::out_ptr(lp);
+    VERIFY( lp == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = &l2;
+
+    const auto& cop = op;
+    VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+  }
+  VERIFY( *lp == 6 );
+}
+
+int main()
+{
+  test_unique_ptr();
+  test_shared_ptr();
+  test_custom_ptr();
+  test_raw_ptr();
+}
diff --git 
a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc
new file mode 100644
index 00000000000..b2532dc7f72
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+
+std::shared_ptr<int> sp;
+auto op = std::out_ptr(sp); // { dg-error "here" }
+// { dg-error "deleter must be used" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc 
b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc
new file mode 100644
index 00000000000..3ff84a3aeaf
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc
@@ -0,0 +1,60 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+std::unique_ptr<int> intptr;
+
+void
+val_out(void** p)
+{
+  // The smart pointer should be reset by the out_ptr_t constructor.
+  VERIFY( intptr == nullptr );
+  // The initial value of the pointer should be null.
+  VERIFY( *p == nullptr );
+  // Although the implementation is allowed to access the unique_ptr's
+  // pointer directly, it can't do that here because the unique_ptr stores
+  // an int* and we are writing to a void*.
+  VERIFY( intptr.get() != *p );
+
+  // Return a heap-allocated int in *p.
+  *p = (void*) new int(1234);
+}
+
+void
+test_out_ptr_void()
+{
+  intptr.reset(new int);
+  val_out(std::out_ptr<void*>(intptr));
+  VERIFY( *intptr == 1234 );
+}
+
+void
+val_inout(void** p)
+{
+  // The smart pointer should have been released by the inout_ptr_t constructor
+  VERIFY( intptr == nullptr );
+  // The initial value of the pointer should be non-null.
+  VERIFY( *(int*)*p == 5678 );
+  // Although the implementation is allowed to access the unique_ptr's
+  // pointer directly, it can't do that here because the unique_ptr stores
+  // an int* and we are reading from and writing to a void*.
+  VERIFY( intptr.get() != *p );
+
+  // Return a heap-allocated int in *p.
+  *p = (void*) new int(999);
+}
+
+void
+test_inout_ptr_void()
+{
+  intptr.reset(new int(5678));
+  val_inout(std::inout_ptr<void*>(intptr));
+  VERIFY( *intptr == 999 );
+}
+
+int main()
+{
+  test_out_ptr_void();
+  test_inout_ptr_void();
+}
-- 
2.41.0

Reply via email to