std::to_address(3)

NAME
std::to_address - std::to_address

Synopsis
   Defined in header <memory>
   template< class Ptr > (1) (since C++20)
   constexpr auto to_address(const Ptr& p) noexcept;
   template< class T > (2) (since C++20)
   constexpr T* to_address(T* p) noexcept;
   Obtain the address represented by p without forming a reference to the object
   pointed to by p.
   1) Fancy pointer overload: If the expression std::pointer_traits<Ptr>::to_address(p)
   is well-formed, returns the result of that expression. Otherwise, returns
   std::to_address(p.operator->()).
   2) Raw pointer overload: If T is a function type, the program is ill-formed.
   Otherwise, returns p unmodified.

Parameters
   p - fancy or raw pointer

Return value
   Raw pointer that represents the same address as p does.

Possible implementation
   template<class T>
   constexpr T* to_address(T* p) noexcept
   {
       static_assert(!std::is_function_v<T>);
       return p;
   }
   template<class T>
   constexpr auto to_address(const T& p) noexcept
   {
       if constexpr (requires{ std::pointer_traits<T>::to_address(p); }) {
           return std::pointer_traits<T>::to_address(p);
       } else {
           return std::to_address(p.operator->());
       }
   }

Notes
   std::to_address can be used even when p does not reference storage that has an
   object constructed in it, in which case std::addressof(*p) cannot be used because
   there's no valid object for the parameter of std::addressof to bind to.
   The fancy pointer overload of to_address inspects the std::pointer_traits<Ptr>
   specialization. If instantiating that specialization is itself ill-formed (typically
   because element_type cannot be defined), that results in a hard error outside the
   immediate context and renders the program ill-formed.
   Feature-test macro: __cpp_lib_to_address

Example
// Run this code
 #include <memory>
 template<class A>
 auto allocator_new(A& a)
 {
     auto p = a.allocate(1);
     try {
         std::allocator_traits<A>::construct(a, std::to_address(p));
     } catch (...) {
         a.deallocate(p, 1);
         throw;
     }
     return p;
 }
 template<class A>
 void allocator_delete(A& a, typename std::allocator_traits<A>::pointer p)
 {
     std::allocator_traits<A>::destroy(a, std::to_address(p));
     a.deallocate(p, 1);
 }
 int main()
 {
     std::allocator<int> a;
     auto p = allocator_new(a);
     allocator_delete(a, p);
 }

See also
   pointer_traits provides information about pointer-like types
   (C++11) (class template)
                        obtains a raw pointer from a fancy pointer (inverse of
   to_address pointer_to)
   [static] (C++20)(optional) (public static member function of
                        std::pointer_traits<Ptr>)