C++2a Utility functions to implement uses-allocator construction (P0591R4)
authorJonathan Wakely <jwakely@redhat.com>
Fri, 1 Mar 2019 13:50:36 +0000 (13:50 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 1 Mar 2019 13:50:36 +0000 (13:50 +0000)
* include/std/memory (uses_allocator_construction_args): New set of
overloaded functions.
(make_obj_using_allocator, uninitialized_construct_using_allocator):
New functions.
* include/std/memory_resource (polymorphic_allocator::construct)
[__cplusplus > 201703l]: Replace all overloads with a single function
using uses_allocator_construction_args.
* testsuite/20_util/polymorphic_allocator/construct_c++2a.cc: New
test.
* testsuite/20_util/uses_allocator/make_obj.cc: New test.

From-SVN: r269311

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/memory
libstdc++-v3/include/std/memory_resource
libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_c++2a.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/uses_allocator/make_obj.cc [new file with mode: 0644]

index 05675919faa8e1fbea9ed12524b928bc4afc413b..67249de5bf5a1cd6b3d44e1ed78c08756dc0e2c7 100644 (file)
@@ -1,3 +1,16 @@
+2019-03-01  Jonathan Wakely  <jwakely@redhat.com>
+
+       * include/std/memory (uses_allocator_construction_args): New set of
+       overloaded functions.
+       (make_obj_using_allocator, uninitialized_construct_using_allocator):
+       New functions.
+       * include/std/memory_resource (polymorphic_allocator::construct)
+       [__cplusplus > 201703l]: Replace all overloads with a single function
+       using uses_allocator_construction_args.
+       * testsuite/20_util/polymorphic_allocator/construct_c++2a.cc: New
+       test.
+       * testsuite/20_util/uses_allocator/make_obj.cc: New test.
+
 2019-02-27  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/89466
index ff9add9cef275c9703086e0dbe0f2febd98995cd..00a85eef25e13aa1a05045ed709d0a25a97021ea 100644 (file)
@@ -91,6 +91,8 @@
 #include <cstdint>
 #if __cplusplus > 201703L
 # include <bit>                        // for ispow2
+# include <new>                        // for placement operator new
+# include <tuple>              // for tuple, make_tuple, make_from_tuple
 #endif
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -166,6 +168,197 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; }
     }
 #endif // C++2a
 
+#if __cplusplus > 201703L
+  template<typename _Tp>
+    struct __is_pair : false_type { };
+  template<typename _Tp, typename _Up>
+    struct __is_pair<pair<_Tp, _Up>> : true_type { };
+  template<typename _Tp, typename _Up>
+    struct __is_pair<const pair<_Tp, _Up>> : true_type { };
+
+  template<typename _Tp, typename __ = _Require<__not_<__is_pair<_Tp>>>,
+          typename _Alloc, typename... _Args>
+    constexpr auto
+    __uses_alloc_args(const _Alloc& __a, _Args&&... __args) noexcept
+    {
+      if constexpr (uses_allocator_v<remove_cv_t<_Tp>, _Alloc>)
+       {
+         if constexpr (is_constructible_v<_Tp, allocator_arg_t,
+                                          const _Alloc&, _Args...>)
+           {
+             return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(
+                 allocator_arg, __a, std::forward<_Args>(__args)...);
+           }
+         else
+           {
+             static_assert(is_constructible_v<_Tp, _Args..., const _Alloc&>);
+
+             return tuple<_Args&&..., const _Alloc&>(
+                 std::forward<_Args>(__args)..., __a);
+           }
+       }
+      else
+       {
+         static_assert(is_constructible_v<_Tp, _Args...>);
+
+         return tuple<_Args&&...>(std::forward<_Args>(__args)...);
+       }
+    }
+
+#if __cpp_concepts
+  template<typename _Tp>
+    concept bool _Std_pair = __is_pair<_Tp>::value;
+#endif
+
+// This is a temporary workaround until -fconcepts is implied by -std=gnu++2a
+#if __cpp_concepts
+# define _GLIBCXX_STD_PAIR_CONSTRAINT(T) _Std_pair T
+# define _GLIBCXX_STD_PAIR_CONSTRAINT_(T) _Std_pair T
+#else
+# define _GLIBCXX_STD_PAIR_CONSTRAINT(T) \
+      typename T, typename __ = _Require<__is_pair<T>>
+# define _GLIBCXX_STD_PAIR_CONSTRAINT_(T) typename T, typename
+#endif
+
+  template<typename _Tp,
+#if ! __cpp_concepts
+          typename __ = _Require<__not_<__is_pair<_Tp>>>,
+#endif
+          typename _Alloc, typename... _Args>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a,
+                                    _Args&&... __args) noexcept
+#if __cpp_concepts
+    requires ! _Std_pair<_Tp>
+#endif
+    {
+      return std::__uses_alloc_args<_Tp>(__a, std::forward<_Args>(__args)...);
+    }
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
+          typename _Tuple1, typename _Tuple2>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
+                                    _Tuple1&& __x, _Tuple2&& __y) noexcept;
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc&) noexcept;
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
+          typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc&, _Up&&, _Vp&&) noexcept;
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
+          typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc&,
+                                    const pair<_Up, _Vp>&) noexcept;
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
+          typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc&, pair<_Up, _Vp>&&) noexcept;
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
+          typename _Tuple1, typename _Tuple2>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
+                                    _Tuple1&& __x, _Tuple2&& __y) noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+         std::apply([&__a](auto&&... __args1) {
+             return std::uses_allocator_construction_args<_Tp1>(
+                 __a, std::forward<decltype(__args1)>(__args1)...);
+         }, std::forward<_Tuple1>(__x)),
+         std::apply([&__a](auto&&... __args2) {
+             return std::uses_allocator_construction_args<_Tp2>(
+                 __a, std::forward<decltype(__args2)>(__args2)...);
+         }, std::forward<_Tuple2>(__y)));
+    }
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a) noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+         std::uses_allocator_construction_args<_Tp1>(__a),
+         std::uses_allocator_construction_args<_Tp2>(__a));
+    }
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
+          typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a, _Up&& __u, _Vp&& __v)
+      noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+         std::uses_allocator_construction_args<_Tp1>(__a,
+           std::forward<_Up>(__u)),
+         std::uses_allocator_construction_args<_Tp2>(__a,
+           std::forward<_Vp>(__v)));
+    }
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
+          typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a,
+                                    const pair<_Up, _Vp>& __pr) noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+         std::uses_allocator_construction_args<_Tp1>(__a, __pr.first),
+         std::uses_allocator_construction_args<_Tp2>(__a, __pr.second));
+    }
+
+  template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
+          typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a,
+                                    pair<_Up, _Vp>&& __pr) noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+         std::uses_allocator_construction_args<_Tp1>(__a,
+           std::move(__pr).first),
+         std::uses_allocator_construction_args<_Tp2>(__a,
+           std::move(__pr).second));
+    }
+
+  template<typename _Tp, typename _Alloc, typename... _Args>
+    inline _Tp
+    make_obj_using_allocator(const _Alloc& __a, _Args&&... __args)
+    {
+      return std::make_from_tuple<_Tp>(uses_allocator_construction_args<_Tp>(
+           __a, std::forward<_Args>(__args)...));
+    }
+
+  template<typename _Tp, typename _Alloc, typename... _Args>
+    inline _Tp*
+    uninitialized_construct_using_allocator(_Tp* __p, const _Alloc& __a,
+                                           _Args&&... __args)
+    {
+      void* __vp = const_cast<void*>(static_cast<const volatile void*>(__p));
+      return ::new(__vp) _Tp(std::make_obj_using_allocator<_Tp>(__a,
+           std::forward<_Args>(__args)...));
+    }
+
+#endif // C++2a
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 #endif // C++11
index 93b2ebd97599a34501b00b6481e76b6e6366ddcb..a212bccc9b18347951f63c7062c3864e95580067 100644 (file)
@@ -170,6 +170,7 @@ namespace pmr
       __attribute__((__nonnull__))
       { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); }
 
+#if __cplusplus <= 201703L
       template<typename _Tp1, typename... _Args>
        __attribute__((__nonnull__))
        typename __not_pair<_Tp1>::type
@@ -242,6 +243,16 @@ namespace pmr
                          forward_as_tuple(std::forward<_Up>(__pr.first)),
                          forward_as_tuple(std::forward<_Vp>(__pr.second)));
        }
+#else
+      template<typename _Tp1, typename... _Args>
+       __attribute__((__nonnull__))
+       void
+       construct(_Tp1* __p, _Args&&... __args)
+       {
+         std::uninitialized_construct_using_allocator(__p, *this,
+             std::forward<_Args>(__args)...);
+       }
+#endif
 
       template<typename _Up>
        __attribute__((__nonnull__))
diff --git a/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_c++2a.cc b/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_c++2a.cc
new file mode 100644 (file)
index 0000000..9048ca1
--- /dev/null
@@ -0,0 +1,125 @@
+// Copyright (C) 2016-2019 Free Software Foundation, Inc.
+//
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <memory_resource>
+#include <utility>
+#include <tuple>
+
+struct do_not_copy {
+  do_not_copy() = default;
+  do_not_copy(const do_not_copy&) { throw 1; }
+};
+
+void
+test01()
+{
+  struct X {
+    X(do_not_copy&&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<do_not_copy>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test02()
+{
+  struct X {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    X(do_not_copy&&, const allocator_type&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<do_not_copy>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test03()
+{
+  struct X {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    X(std::allocator_arg_t, const allocator_type&, do_not_copy&&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<do_not_copy>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test04()
+{
+  struct X
+  {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    X() = default;
+    X(const X&) { throw 1; }
+    X(const X&, const allocator_type&) { }
+  };
+
+  struct Y
+  {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    Y() = default;
+    Y(const Y&) = delete;
+    Y(std::allocator_arg_t, const allocator_type&, const Y&) { }
+  };
+
+  using pair_type = std::pair<X, Y>;
+  std::pmr::polymorphic_allocator<pair_type> a;
+  auto ptr = a.allocate(1);
+  /* not const */ pair_type p;
+  a.construct(ptr, p); // LWG 2975
+  a.deallocate(ptr, 1);
+}
+
+void
+test05()
+{
+  struct X {
+    using allocator_type = std::pmr::polymorphic_allocator<char>;
+    X(int);
+    X(int, const allocator_type&) { }
+  };
+  std::pmr::polymorphic_allocator<X> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, 1);
+  a.deallocate(ptr, 1);
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/20_util/uses_allocator/make_obj.cc b/libstdc++-v3/testsuite/20_util/uses_allocator/make_obj.cc
new file mode 100644 (file)
index 0000000..670c59a
--- /dev/null
@@ -0,0 +1,403 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+using test_allocator = __gnu_test::uneq_allocator<int>;
+
+struct Arg { };
+
+struct A
+{
+  A() : nargs(0) { }
+  A(float&) : nargs(1) { }
+  A(int, void*) : nargs(2) { }
+
+  // These should not be used:
+  A(const test_allocator& a);
+  A(float&, const test_allocator& a);
+  A(int, void*, const test_allocator& a);
+
+  const int nargs;
+  const int alloc_id = -1;
+
+  // std::uses_allocator<A, test_allocator> should be false:
+  using allocator_type = void*();
+};
+
+struct B
+{
+  // This means std::uses_allocator<B, test_allocator> is true:
+  using allocator_type = test_allocator;
+
+  B() : nargs(0) { }
+  B(float&) : nargs(1) { }
+  B(int, void*) : nargs(2) { }
+
+  B(std::allocator_arg_t, const test_allocator& a)
+  : nargs(0), alloc_id(a.get_personality()) { }
+  B(std::allocator_arg_t, const test_allocator& a, float&)
+  : nargs(1), alloc_id(a.get_personality()) { }
+  B(std::allocator_arg_t, const test_allocator& a, int, void*)
+  : nargs(2), alloc_id(a.get_personality()) { }
+  B(std::allocator_arg_t, const test_allocator& a, B&& b)
+  : nargs(b.nargs), alloc_id(a.get_personality()) { }
+
+  // These should not be used:
+  B(const test_allocator&);
+  B(float&, const test_allocator&, float&);
+  B(int, void*, const test_allocator&);
+  B(const test_allocator&, float&);
+  B(const test_allocator&, int, void*);
+  B(B&&);
+  B(B&&, const test_allocator&);
+
+  const int nargs;
+  const int alloc_id = -1;
+};
+
+struct C
+{
+  C() : nargs(0) { }
+  C(float&) : nargs(1) { }
+  C(int, void*) : nargs(2) { }
+
+  C(const test_allocator& a)
+  : nargs(0), alloc_id(a.get_personality()) { }
+  C(float&, const test_allocator& a)
+  : nargs(1), alloc_id(a.get_personality()) { }
+  C(int, void*, const test_allocator& a)
+  : nargs(2), alloc_id(a.get_personality()) { }
+  C(C&& c, const test_allocator& a)
+  : nargs(c.nargs), alloc_id(a.get_personality()) { }
+
+  C(C&&);
+
+  const int nargs;
+  const int alloc_id = -1;
+};
+
+namespace std {
+  // This means std::uses_allocator<C, test_allocator> is true:
+  template<> struct uses_allocator<C, test_allocator> : std::true_type { };
+}
+
+test_allocator alloc1(1);
+test_allocator alloc2(2);
+
+void
+test01()
+{
+  auto i0 = std::make_obj_using_allocator<int>(alloc1, 2);
+  VERIFY( i0 == 2 );
+
+  float f = 0.0f;
+
+  auto a0 = std::make_obj_using_allocator<A>(alloc1);
+  VERIFY( a0.nargs == 0 );
+  VERIFY( a0.alloc_id == -1 );
+  auto a1 = std::make_obj_using_allocator<A>(alloc1, f);
+  VERIFY( a1.nargs == 1 );
+  VERIFY( a1.alloc_id == -1 );
+  auto a2 = std::make_obj_using_allocator<A>(alloc1, 123, nullptr);
+  VERIFY( a2.nargs == 2 );
+  VERIFY( a2.alloc_id == -1 );
+
+  auto b0 = std::make_obj_using_allocator<B>(alloc1);
+  VERIFY( b0.nargs == 0 );
+  VERIFY( b0.alloc_id == 1 );
+  auto b1 = std::make_obj_using_allocator<B>(alloc2, f);
+  VERIFY( b1.nargs == 1 );
+  VERIFY( b1.alloc_id == 2 );
+  auto b2 = std::make_obj_using_allocator<B>(alloc1, 123, nullptr);
+  VERIFY( b2.nargs == 2 );
+  VERIFY( b2.alloc_id == 1 );
+
+  auto c0 = std::make_obj_using_allocator<C>(alloc1);
+  VERIFY( c0.nargs == 0 );
+  VERIFY( c0.alloc_id == 1 );
+  auto c1 = std::make_obj_using_allocator<C>(alloc2, f);
+  VERIFY( c1.nargs == 1 );
+  VERIFY( c1.alloc_id == 2 );
+  auto c2 = std::make_obj_using_allocator<C>(alloc1, 123, nullptr);
+  VERIFY( c2.nargs == 2 );
+  VERIFY( c2.alloc_id == 1 );
+}
+
+void 
+test02()
+{
+  decltype(auto) b
+    = std::make_obj_using_allocator<const B>(alloc1, 123, nullptr);
+  static_assert( std::is_const_v<decltype(b)> );
+  VERIFY( b.nargs == 2 );
+  VERIFY( b.alloc_id == 1 );
+
+  decltype(auto) c = std::make_obj_using_allocator<const C>(alloc1);
+  static_assert( std::is_const_v<decltype(c)> );
+  VERIFY( c.nargs == 0 );
+  VERIFY( c.alloc_id == 1 );
+}
+
+void
+test03()
+{
+  B b;
+  decltype(auto) ref = std::make_obj_using_allocator<B&>(alloc1, b);
+  static_assert( std::is_same_v<decltype(ref), B&> );
+  VERIFY( &ref == &b );
+  VERIFY( ref.nargs == 0 );
+  VERIFY( ref.alloc_id == -1 );
+  const B& cref = std::make_obj_using_allocator<const B&>(alloc1, b);
+  static_assert( std::is_same_v<decltype(cref), const B&> );
+  VERIFY( &cref == &b );
+  VERIFY( cref.nargs == 0 );
+  VERIFY( cref.alloc_id == -1 );
+}
+
+void
+test04()
+{
+  struct D
+  {
+    D(std::allocator_arg_t) { }
+    D(std::allocator_arg_t, int) { }
+
+    // These should not be used:
+    D(std::allocator_arg_t, const test_allocator&);
+    D(std::allocator_arg_t, const test_allocator&, int);
+
+    ~D() { }
+  };
+
+  D d1 = std::make_obj_using_allocator<D>(alloc1, std::allocator_arg);
+
+  struct E
+  {
+    using allocator_type = test_allocator;
+
+    E(std::allocator_arg_t, const test_allocator&) { }
+    E(std::allocator_arg_t, int, const test_allocator&) { }
+
+    // These should not be used:
+    E(std::allocator_arg_t);
+    E(std::allocator_arg_t, int);
+
+    ~E() { }
+  };
+
+  E e1 = std::make_obj_using_allocator<E>(alloc1, std::allocator_arg);
+  E e2 = std::make_obj_using_allocator<E>(alloc2, std::allocator_arg, 2);
+}
+
+void
+test05()
+{
+  using std::pair;
+  std::piecewise_construct_t p;
+  std::tuple<> t0;
+  float f = 0.0f;
+  std::tuple<float&> t1(f);
+  std::tuple<int, void*> t2{};
+
+  auto aa00 = std::make_obj_using_allocator<pair<A, A>>(alloc1, p, t0, t0);
+  VERIFY( aa00.first.nargs == 0 );
+  VERIFY( aa00.first.alloc_id == -1 );
+  VERIFY( aa00.second.nargs == 0 );
+  VERIFY( aa00.second.alloc_id == -1 );
+  auto ab00 = std::make_obj_using_allocator<pair<A, B>>(alloc1, p, t0, t0);
+  VERIFY( ab00.first.nargs == 0 );
+  VERIFY( ab00.first.alloc_id == -1 );
+  VERIFY( ab00.second.nargs == 0 );
+  VERIFY( ab00.second.alloc_id == 1 );
+  auto bc00 = std::make_obj_using_allocator<pair<B, C>>(alloc2, p, t0, t0);
+  VERIFY( bc00.first.nargs == 0 );
+  VERIFY( bc00.first.alloc_id == 2 );
+  VERIFY( bc00.second.nargs == 0 );
+  VERIFY( bc00.second.alloc_id == 2 );
+  auto cb00 = std::make_obj_using_allocator<pair<C, B>>(alloc2, p, t0, t0);
+  VERIFY( cb00.first.nargs == 0 );
+  VERIFY( cb00.first.alloc_id == 2 );
+  VERIFY( cb00.second.nargs == 0 );
+  VERIFY( cb00.second.alloc_id == 2 );
+  auto cc00
+    = std::make_obj_using_allocator<pair<C, const C>>(alloc1, p, t0, t0);
+  VERIFY( cc00.first.nargs == 0 );
+  VERIFY( cc00.first.alloc_id == 1 );
+  VERIFY( cc00.second.nargs == 0 );
+  VERIFY( cc00.second.alloc_id == 1 );
+
+  auto aa21 = std::make_obj_using_allocator<pair<A, A>>(alloc1, p, t2, t1);
+  VERIFY( aa21.first.nargs == 2 );
+  VERIFY( aa21.first.alloc_id == -1 );
+  VERIFY( aa21.second.nargs == 1 );
+  VERIFY( aa21.second.alloc_id == -1 );
+  auto ab21 = std::make_obj_using_allocator<pair<A, B>>(alloc1, p, t2, t1);
+  VERIFY( ab21.first.nargs == 2 );
+  VERIFY( ab21.first.alloc_id == -1 );
+  VERIFY( ab21.second.nargs == 1 );
+  VERIFY( ab21.second.alloc_id == 1 );
+  auto bc11 = std::make_obj_using_allocator<pair<B, C>>(alloc2, p, t1, t1);
+  VERIFY( bc11.first.nargs == 1 );
+  VERIFY( bc11.first.alloc_id == 2 );
+  VERIFY( bc11.second.nargs == 1 );
+  VERIFY( bc11.second.alloc_id == 2 );
+  auto cb12 = std::make_obj_using_allocator<pair<C, B>>(alloc2, p, t1, t2);
+  VERIFY( cb12.first.nargs == 1 );
+  VERIFY( cb12.first.alloc_id == 2 );
+  VERIFY( cb12.second.nargs == 2 );
+  VERIFY( cb12.second.alloc_id == 2 );
+  auto cc22
+    = std::make_obj_using_allocator<pair<C, const C>>(alloc1, p, t2, t1);
+  VERIFY( cc22.first.nargs == 2 );
+  VERIFY( cc22.first.alloc_id == 1 );
+  VERIFY( cc22.second.nargs == 1 );
+  VERIFY( cc22.second.alloc_id == 1 );
+}
+
+void
+test06()
+{
+  using std::pair;
+  float f = 0.0f;
+
+  auto aa00 = std::make_obj_using_allocator<pair<A, A>>(alloc1);
+  VERIFY( aa00.first.nargs == 0 );
+  VERIFY( aa00.first.alloc_id == -1 );
+  VERIFY( aa00.second.nargs == 0 );
+  VERIFY( aa00.second.alloc_id == -1 );
+  auto ab00 = std::make_obj_using_allocator<pair<A, B>>(alloc1);
+  VERIFY( ab00.first.nargs == 0 );
+  VERIFY( ab00.first.alloc_id == -1 );
+  VERIFY( ab00.second.nargs == 0 );
+  VERIFY( ab00.second.alloc_id == 1 );
+  auto bc00 = std::make_obj_using_allocator<pair<B, C>>(alloc2);
+  VERIFY( bc00.first.nargs == 0 );
+  VERIFY( bc00.first.alloc_id == 2 );
+  VERIFY( bc00.second.nargs == 0 );
+  VERIFY( bc00.second.alloc_id == 2 );
+  auto cb00 = std::make_obj_using_allocator<pair<C, B>>(alloc2);
+  VERIFY( cb00.first.nargs == 0 );
+  VERIFY( cb00.first.alloc_id == 2 );
+  VERIFY( cb00.second.nargs == 0 );
+  VERIFY( cb00.second.alloc_id == 2 );
+  auto cc00 = std::make_obj_using_allocator<pair<C, const C>>(alloc1);
+  VERIFY( cc00.first.nargs == 0 );
+  VERIFY( cc00.first.alloc_id == 1 );
+  VERIFY( cc00.second.nargs == 0 );
+  VERIFY( cc00.second.alloc_id == 1 );
+
+  auto aa11 = std::make_obj_using_allocator<pair<A, A>>(alloc1, f, f);
+  VERIFY( aa11.first.nargs == 1 );
+  VERIFY( aa11.first.alloc_id == -1 );
+  VERIFY( aa11.second.nargs == 1 );
+  VERIFY( aa11.second.alloc_id == -1 );
+  auto aba1 = std::make_obj_using_allocator<pair<A, B>>(alloc1, A{}, f);
+  VERIFY( aba1.first.nargs == 0 );
+  VERIFY( aba1.first.alloc_id == -1 );
+  VERIFY( aba1.second.nargs == 1 );
+  VERIFY( aba1.second.alloc_id == 1 );
+  auto bc11 = std::make_obj_using_allocator<pair<B, C>>(alloc2, f, f);
+  VERIFY( bc11.first.nargs == 1 );
+  VERIFY( bc11.first.alloc_id == 2 );
+  VERIFY( bc11.second.nargs == 1 );
+  VERIFY( bc11.second.alloc_id == 2 );
+  auto cb1b = std::make_obj_using_allocator<pair<C, B>>(alloc2, f, B{});
+  VERIFY( cb1b.first.nargs == 1 );
+  VERIFY( cb1b.first.alloc_id == 2 );
+  VERIFY( cb1b.second.nargs == 0 );
+  VERIFY( cb1b.second.alloc_id == 2 );
+  auto cccc
+    = std::make_obj_using_allocator<pair<C, const C>>(alloc1, C{}, C{});
+  VERIFY( cccc.first.nargs == 0 );
+  VERIFY( cccc.first.alloc_id == 1 );
+  VERIFY( cccc.second.nargs == 0 );
+  VERIFY( cccc.second.alloc_id == 1 );
+
+  pair<float&, A> p1a(f, A{});
+  pair<float&, float&> p11(f, f);
+  auto aa1a = std::make_obj_using_allocator<pair<A, A>>(alloc1, p1a);
+  VERIFY( aa1a.first.nargs == 1 );
+  VERIFY( aa1a.first.alloc_id == -1 );
+  VERIFY( aa1a.second.nargs == 0 );
+  VERIFY( aa1a.second.alloc_id == -1 );
+  auto ab11 = std::make_obj_using_allocator<pair<A, B>>(alloc1, p11);
+  VERIFY( ab11.first.nargs == 1 );
+  VERIFY( ab11.first.alloc_id == -1 );
+  VERIFY( ab11.second.nargs == 1 );
+  VERIFY( ab11.second.alloc_id == 1 );
+  auto cb11 = std::make_obj_using_allocator<pair<C, B>>(alloc2, p11);
+  VERIFY( cb11.first.nargs == 1 );
+  VERIFY( cb11.first.alloc_id == 2 );
+  VERIFY( cb11.second.nargs == 1 );
+  VERIFY( cb11.second.alloc_id == 2 );
+
+  auto bcbc = std::make_obj_using_allocator<pair<B, C>>(alloc2, pair<B, C>());
+  VERIFY( bcbc.first.nargs == 0 );
+  VERIFY( bcbc.first.alloc_id == 2 );
+  VERIFY( bcbc.second.nargs == 0 );
+  VERIFY( bcbc.second.alloc_id == 2 );
+
+  auto cc11 = std::make_obj_using_allocator<pair<C, B>>(alloc2, std::move(p11));
+  VERIFY( cc11.first.nargs == 1 );
+  VERIFY( cc11.first.alloc_id == 2 );
+  VERIFY( cc11.second.nargs == 1 );
+  VERIFY( cc11.second.alloc_id == 2 );
+}
+
+void
+test07()
+{
+  using nested_pair = std::pair<const std::pair<B, const B>, C>;
+  auto p = std::make_obj_using_allocator<const nested_pair>(alloc1);
+  VERIFY( p.first.first.alloc_id == 1 );
+  VERIFY( p.first.second.alloc_id == 1 );
+  VERIFY( p.second.alloc_id == 1 );
+}
+
+void
+test08()
+{
+  // LWG DR 3187.
+  // P0591R4 reverted DR 2586 fixes to scoped_allocator_adaptor::construct()
+
+  struct X {
+    using allocator_type = std::allocator<X>;
+    X(std::allocator_arg_t, allocator_type&&) { }
+    X(const allocator_type&) { }
+  };
+
+  std::allocator<X> a;
+  std::make_obj_using_allocator<X>(a);
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+  test08();
+}