[libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted `pair` constructor overloads (PR #164214)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Mon Oct 20 01:17:20 PDT 2025


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/164214

>From 8e3073bbee02101dc20dad6fe560449ff1de506c Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 20 Oct 2025 15:56:24 +0800
Subject: [PATCH] [libc++] P2255R2: Add deleted `pair` constructor overloads

Implements parts for `std::pair` from P2255R2 "A type trait to detect
reference binding to temporary".
---
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/__utility/pair.h               |  76 +++++-
 .../flat.map/flat.map.observers/comp.pass.cpp |   2 +-
 .../flat.multimap.observers/comp.pass.cpp     |   2 +-
 .../pairs/pairs.pair/ctor.U_V.pass.cpp        |  12 +-
 .../pairs/pairs.pair/ctor.deleted.verify.cpp  | 225 ++++++++++++++++++
 .../ctor.pair_U_V_const_move.pass.cpp         |   8 +
 .../ctor.pair_U_V_const_ref.pass.cpp          |  13 +-
 .../pairs.pair/ctor.pair_U_V_move.pass.cpp    |  10 +
 .../pairs.pair/ctor.pair_U_V_ref.pass.cpp     |   8 +
 .../pairs/pairs.pair/ctor.pair_like.pass.cpp  |  46 ++++
 11 files changed, 391 insertions(+), 13 deletions(-)
 create mode 100644 libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.deleted.verify.cpp

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 3a87e64339e1f..030f85e1315ab 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -43,7 +43,7 @@
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15","`#105175 <https://github.com/llvm/llvm-project/issues/105175>`__",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17","`#105176 <https://github.com/llvm/llvm-project/issues/105176>`__",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","`#105177 <https://github.com/llvm/llvm-project/issues/105177>`__","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."
-"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits only."
+"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits and changes to ``std::pair`` only."
 "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 <https://github.com/llvm/llvm-project/issues/105182>`__",""
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 33694c52430f1..7ab475a49ab38 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -36,6 +36,7 @@
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/nat.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
 #include <__type_traits/unwrap_ref.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
@@ -68,9 +69,23 @@ struct __check_pair_construction {
 
   template <class _U1, class _U2>
   static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_pair_constructible() {
+#  if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+    return is_constructible_v<_T1, _U1> && is_constructible_v<_T2, _U2> &&
+           !reference_constructs_from_temporary_v<_T1, _U1&&> && !reference_constructs_from_temporary_v<_T2, _U2&&>;
+#  else
     return is_constructible<_T1, _U1>::value && is_constructible<_T2, _U2>::value;
+#  endif
   }
 
+#  if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  template <class _U1, class _U2>
+  static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_pair_constructor_deleted() {
+    return is_constructible_v<_T1, _U1> && is_constructible_v<_T2, _U2> &&
+           (reference_constructs_from_temporary_v<_T1, _U1&&> || reference_constructs_from_temporary_v<_T2, _U2&&>);
+    return is_constructible<_T1, _U1>::value && is_constructible<_T2, _U2>::value;
+  }
+#  endif
+
   template <class _U1, class _U2>
   static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_implicit() {
     return is_convertible<_U1, _T1>::value && is_convertible<_U2, _T2>::value;
@@ -157,14 +172,20 @@ struct pair
       class _U1,
       class _U2,
 #  endif
-      __enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1, _U2>(), int> = 0 >
+      __enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1&&, _U2&&>(), int> = 0 >
   _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1, _U2>())
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1&&, _U2&&>())
       pair(_U1&& __u1, _U2&& __u2) noexcept(is_nothrow_constructible<first_type, _U1>::value &&
                                             is_nothrow_constructible<second_type, _U2>::value)
       : first(std::forward<_U1>(__u1)), second(std::forward<_U2>(__u2)) {
   }
 
+#  if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  template <class _U1 = _T1, class _U2 = _T2>
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructor_deleted<_U1 &&, _U2 &&>())
+  explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1&&, _U2&&>()) pair(_U1&&, _U2&&) = delete;
+#  endif
+
 #  if _LIBCPP_STD_VER >= 23
   template <class _U1,
             class _U2,
@@ -173,6 +194,12 @@ struct pair
       pair(pair<_U1, _U2>& __p) noexcept((is_nothrow_constructible<first_type, _U1&>::value &&
                                           is_nothrow_constructible<second_type, _U2&>::value))
       : first(__p.first), second(__p.second) {}
+
+#    if __has_builtin(__reference_constructs_from_temporary)
+  template <class _U1, class _U2>
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructor_deleted<_U1&, _U2&>())
+  explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1&, _U2&>()) pair(pair<_U1, _U2>&) = delete;
+#    endif
 #  endif
 
   template <
@@ -186,15 +213,30 @@ struct pair
                                                is_nothrow_constructible<second_type, _U2 const&>::value)
       : first(__p.first), second(__p.second) {}
 
-  template <class _U1,
-            class _U2,
-            __enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1, _U2>(), int> = 0>
+#  if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  template <class _U1, class _U2>
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructor_deleted<const _U1&, const _U2&>())
+  explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<const _U1&, const _U2&>())
+      pair(const pair<_U1, _U2>&) = delete;
+#  endif
+
+  template <
+      class _U1,
+      class _U2,
+      __enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1&&, _U2&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1, _U2>())
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1&&, _U2&&>())
       pair(pair<_U1, _U2>&& __p) noexcept(is_nothrow_constructible<first_type, _U1&&>::value &&
                                           is_nothrow_constructible<second_type, _U2&&>::value)
       : first(std::forward<_U1>(__p.first)), second(std::forward<_U2>(__p.second)) {}
 
+#  if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  template <class _U1, class _U2>
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructor_deleted<_U1 &&, _U2 &&>())
+  explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1&&, _U2&&>())
+      pair(pair<_U1, _U2>&&) = delete;
+#  endif
+
 #  if _LIBCPP_STD_VER >= 23
   template <
       class _U1,
@@ -206,16 +248,34 @@ struct pair
       pair(const pair<_U1, _U2>&& __p) noexcept(is_nothrow_constructible<first_type, const _U1&&>::value &&
                                                 is_nothrow_constructible<second_type, const _U2&&>::value)
       : first(std::move(__p.first)), second(std::move(__p.second)) {}
+
+#    if __has_builtin(__reference_constructs_from_temporary)
+  template <class _U1, class _U2>
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructor_deleted<const _U1 &&, const _U2 &&>())
+  explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<const _U1&&, const _U2&&>())
+      pair(const pair<_U1, _U2>&&) = delete;
+#    endif
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <__pair_like_no_subrange _PairLike>
-    requires(is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike &&>()))> &&
-             is_constructible_v<second_type, decltype(std::get<1>(std::declval<_PairLike &&>()))>)
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructible<
+                decltype(std::get<0>(std::declval<_PairLike &&>())),
+                decltype(std::get<1>(std::declval<_PairLike &&>()))>())
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(
       !is_convertible_v<decltype(std::get<0>(std::declval<_PairLike&&>())), first_type> ||
       !is_convertible_v<decltype(std::get<1>(std::declval<_PairLike&&>())), second_type>) pair(_PairLike&& __p)
       : first(std::get<0>(std::forward<_PairLike>(__p))), second(std::get<1>(std::forward<_PairLike>(__p))) {}
+
+#    if __has_builtin(__reference_constructs_from_temporary)
+  template <__pair_like_no_subrange _PairLike>
+    requires(__check_pair_construction<_T1, _T2>::template __is_pair_constructor_deleted<
+                decltype(std::get<0>(std::declval<_PairLike &&>())),
+                decltype(std::get<1>(std::declval<_PairLike &&>()))>())
+  explicit(!is_convertible_v<decltype(std::get<0>(std::declval<_PairLike&&>())), first_type> ||
+           !is_convertible_v<decltype(std::get<1>(std::declval<_PairLike&&>())), second_type>)
+      pair(_PairLike&&) = delete;
+#    endif
 #  endif
 
   template <class... _Args1, class... _Args2>
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.observers/comp.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.observers/comp.pass.cpp
index 5712493740bc8..626f6ad64c635 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.observers/comp.pass.cpp
@@ -35,7 +35,7 @@ constexpr bool test() {
     assert(kc(1, 2));
     assert(!kc(2, 1));
     auto vc = m.value_comp();
-    ASSERT_SAME_TYPE(decltype(vc(std::make_pair(1, 2), std::make_pair(1, 2))), bool);
+    ASSERT_SAME_TYPE(decltype(vc(std::make_pair(1, '2'), std::make_pair(1, '2'))), bool);
     assert(vc({1, '2'}, {2, '1'}));
     assert(!vc({2, '1'}, {1, '2'}));
   }
diff --git a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.observers/comp.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.observers/comp.pass.cpp
index 070fbb0244e63..2b52b4722f347 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.observers/comp.pass.cpp
@@ -36,7 +36,7 @@ constexpr bool test() {
     assert(kc(1, 2));
     assert(!kc(2, 1));
     auto vc = m.value_comp();
-    ASSERT_SAME_TYPE(decltype(vc(std::make_pair(1, 2), std::make_pair(1, 2))), bool);
+    ASSERT_SAME_TYPE(decltype(vc(std::make_pair(1, '2'), std::make_pair(1, '2'))), bool);
     assert(vc({1, '2'}, {2, '1'}));
     assert(!vc({2, '1'}, {1, '2'}));
   }
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.U_V.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.U_V.pass.cpp
index 33b5711e22183..63b254d102041 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.U_V.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.U_V.pass.cpp
@@ -139,5 +139,15 @@ int main(int, char**)
     }
 #endif // TEST_STD_VER > 20
 
-    return 0;
+// Test construction prohibition of introduced by https://wg21.link/P2255R2.
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  test_sfinae<int&&, char, false>();
+  test_sfinae<const int&, char, false>();
+  test_sfinae<ConvertingType&&, int, false>();
+  test_sfinae<const ConvertingType&, char, false>();
+  test_sfinae<ExplicitTypes::ConvertingType&&, int, false>();
+  test_sfinae<const ExplicitTypes::ConvertingType&, int, false>();
+#endif // TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+
+  return 0;
 }
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.deleted.verify.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.deleted.verify.cpp
new file mode 100644
index 0000000000000..91a9585d3fac7
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.deleted.verify.cpp
@@ -0,0 +1,225 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++11
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+
+// template<class U1 = T1, class U2 = T2>
+//   constexpr explicit(see below) pair(U1&& x, U2&& y);            // since C++11
+
+// The constructor is defined as deleted if
+// reference_constructs_from_temporary_v<first_type, U1&&> || reference_constructs_from_temporary_v<second_type, U2>
+// is true. (since C++23)
+
+// template<class U1, class U2>
+//   constexpr explicit(see below) pair(pair<U1, U2>& p);           // since C++23
+// template<class U1, class U2>
+//   constexpr explicit(see below) pair(const pair<U1, U2>& p);     // since C++11
+// template<class U1, class U2>
+//   constexpr explicit(see below) pair(pair<U1, U2>&& p);          // since C++11
+// template<class U1, class U2>
+//   constexpr explicit(see below) pair(const pair<U1, U2>&& p);    // since C++23
+// template<pair-like P>
+//   constexpr explicit(see below) pair(P&& p);                     // since C++23
+
+// The constructor is defined as deleted if
+// reference_constructs_from_temporary_v<first_type, decltype(get<0>(FWD(p)))> ||
+// reference_constructs_from_temporary_v<second_type, decltype(get<1>(FWD(p)))>
+// is true. (since C++23)
+
+// Such reference binding used to cause hard error for these constructors before C++23 due to CWG1696.
+
+#include <array>
+#include <complex>
+#include <tuple>
+#include <utility>
+
+#include "test_macros.h"
+
+void verify_two_arguments() {
+  std::pair<const long&, int&&> p1{'a', 'b'};
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{call to deleted constructor of 'std::pair<const long &, int &&>'}}
+#else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+  // expected-error at -5 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
+
+#if TEST_STD_VER >= 23
+  std::pair<const long, int&&> p2({42L}, 'c');
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{call to deleted constructor of 'std::pair<const long, int &&>'}}
+#  else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+  std::pair<const long&, int> p3{'d', {}};
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{call to deleted constructor of 'std::pair<const long &, int>'}}
+#  else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+#endif
+}
+
+void verify_pair_const_lvalue() {
+  const std::pair<char, int> src1{'a', 'b'};
+  std::pair<const long&, const int&> dst1 = src1;
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
+
+  const std::pair<long, char> src2{'a', 'b'};
+  std::pair<const long&, const int&> dst2 = src2;
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{conversion function from 'const pair<long, char>' to 'pair<const long &, const int &>' invokes a deleted function}}
+#else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
+}
+
+void verify_pair_rvalue() {
+  std::pair<char, int> src1{'a', 'b'};
+  std::pair<const long&, int&&> dst1 = std::move(src1);
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
+
+  std::pair<long, char> src2{'a', 'b'};
+  std::pair<const long&, int&&> dst2 = std::move(src2);
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
+}
+
+#if TEST_STD_VER >= 23
+void verify_pair_lvalue() {
+  std::pair<char, int> src1{'a', 'b'};
+  std::pair<const long&, int&> dst1 = src1;
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+  std::pair<long, char> src2{'a', 'b'};
+  std::pair<const long&, int&&> dst2 = src2;
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+}
+
+void verify_pair_const_rvalue() {
+  const std::pair<char, int> src1{'a', 'b'};
+  std::pair<const long&, const int&&> dst1 = std::move(src1);
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+  const std::pair<long, char> src2{'a', 'b'};
+  std::pair<const long&, const int&&> dst2 = std::move(src2);
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+}
+
+void verify_pair_like() {
+  std::pair<const long&, int&&> p1 = std::make_tuple('a', int{'b'});
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+  std::pair<const long&, int&&> p2 = std::make_tuple(long{'a'}, 'b');
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+  std::pair<const char&, int&&> p3 = std::array<char, 2>{'a', 'b'};
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+  std::pair<const long&, char&&> p4 = std::array<char, 2>{'a', 'b'};
+#  if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#  else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#  endif
+
+#  if TEST_STD_VER >= 26
+  std::pair<const long double&, float&&> p5 = std::complex<float>{42.0f, 1729.0f};
+#    if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#    else
+  // expected-error at -4 {{reference member 'first' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#    endif
+
+  std::pair<const float&, double&&> p6 = std::complex<float>{3.14159f, 2.71828f};
+#    if __has_builtin(__reference_constructs_from_temporary)
+  // expected-error at -2 {{invokes a deleted function}}
+#    else
+  // expected-error at -4 {{reference member 'second' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#    endif
+#  endif
+}
+#endif
+
+// Verify that copy-non-list-initialization ignores explicit but deleted overloads.
+void verify_explicity() {
+  struct ExplicitlyToInt {
+    explicit operator int() const;
+  };
+
+  const std::pair<int, ExplicitlyToInt> src1;
+  std::pair<int, int&&> dst1 = src1; // expected-error {{no viable conversion}}
+
+  std::pair<int, ExplicitlyToInt> src2;
+  std::pair<int, int&&> dst2 = std::move(src2); // expected-error {{no viable conversion}}
+
+#if TEST_STD_VER >= 23
+  const std::pair<int, ExplicitlyToInt> src3;
+  std::pair<int, int&&> dst3 = std::move(src3); // expected-error {{no viable conversion}}
+
+  std::pair<int, ExplicitlyToInt> src4;
+  std::pair<int, int&&> dst4 = src4; // expected-error {{no viable conversion}}
+
+  std::pair<int, int&&> dst5 = std::make_tuple(0, ExplicitlyToInt{}); // expected-error {{no viable conversion}}
+
+  std::pair<int&&, int> dst6 = std::array<ExplicitlyToInt, 2>{}; // expected-error {{no viable conversion}}
+#endif
+
+#if TEST_STD_VER >= 26
+  struct ExplicitlyFromFloat {
+    explicit ExplicitlyFromFloat(float);
+  };
+
+  std::pair<ExplicitlyFromFloat, const ExplicitlyFromFloat&> dst7 = // expected-error {{no viable conversion}}
+      std::complex<float>{};
+#endif
+}
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_move.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_move.pass.cpp
index 63f409b332878..89eb6ab9dcea2 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_move.pass.cpp
@@ -47,6 +47,14 @@ static_assert(!std::is_convertible_v<const std::pair<X, Y>&&,
                                      std::pair<ExplicitConstructibleFrom<X>, ExplicitConstructibleFrom<Y>>>);
 // clang-format on
 
+// Test construction prohibition of introduced by https://wg21.link/P2255R2.
+#if __has_builtin(__reference_constructs_from_temporary)
+static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, const std::pair<int, short>&&>);
+static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, const std::pair<char, long>&&>);
+static_assert(!std::is_convertible_v<const std::pair<int, short>&&, std::pair<const int&, const long&>>);
+static_assert(!std::is_convertible_v<const std::pair<char, long>&&, std::pair<const int&, const long&>>);
+#endif // __has_builtin(__reference_constructs_from_temporary)
+
 constexpr bool test() {
   // simple case: init pair<const T&&, const U&&> from const pair<T, U>&&
   {
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_ref.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_ref.pass.cpp
index 3a1e9ad08fb14..25ccc14428474 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_ref.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_const_ref.pass.cpp
@@ -189,7 +189,18 @@ TEST_CONSTEXPR_CXX20 bool test() {
       static_assert(p2.second.value == 101, "");
     }
 #endif
-    return true;
+
+// Test construction prohibition of introduced by https://wg21.link/P2255R2.
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  test_pair_const<int&&, char, false>();
+  test_pair_const<const int&, char, false>();
+  test_pair_const<ConvertingType&&, int, false>();
+  test_pair_const<const ConvertingType&, char, false>();
+  test_pair_const<ExplicitTypes::ConvertingType&&, int, false>();
+  test_pair_const<const ExplicitTypes::ConvertingType&, int, false>();
+#endif // TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+
+  return true;
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_move.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_move.pass.cpp
index 8ba9b5696eff1..ad144b0b8cac7 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_move.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_move.pass.cpp
@@ -244,5 +244,15 @@ int main(int, char**)
     }
 #endif
 
+// Test construction prohibition of introduced by https://wg21.link/P2255R2.
+#if TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+  test_pair_rv<int&&, char, false>();
+  test_pair_rv<const int&, char, false>();
+  test_pair_rv<ConvertingType&&, int, false>();
+  test_pair_rv<const ConvertingType&, char, false>();
+  test_pair_rv<ExplicitTypes::ConvertingType&&, int, false>();
+  test_pair_rv<const ExplicitTypes::ConvertingType&, int, false>();
+#endif // TEST_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary)
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_ref.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_ref.pass.cpp
index 7ac70bf36a780..42d338b756917 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_ref.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_U_V_ref.pass.cpp
@@ -42,6 +42,14 @@ static_assert(!std::is_convertible_v<std::pair<X, Y>&, std::pair<ExplicitConstru
 static_assert(!std::is_convertible_v<std::pair<X, Y>&, std::pair<ExplicitConstructibleFrom<X>, ExplicitConstructibleFrom<Y>>>);
 // clang-format on
 
+// Test construction prohibition of introduced by https://wg21.link/P2255R2.
+#if __has_builtin(__reference_constructs_from_temporary)
+static_assert(!std::is_constructible_v<std::pair<const int&, long&&>, std::pair<const char, const long>&>);
+static_assert(!std::is_constructible_v<std::pair<const short&, long&&>, std::pair<const short, const char>&>);
+static_assert(!std::is_convertible_v<std::pair<const char, const long>&, std::pair<const int&, long&&>>);
+static_assert(!std::is_convertible_v<std::pair<const short, const char>&, std::pair<const short&, long&&>>);
+#endif // __has_builtin(__reference_constructs_from_temporary)
+
 constexpr bool test() {
   // use case in zip. Init pair<T&, U&> from pair<T, U>&
   {
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp
index 135045d831d90..1ddfa5d03c810 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp
@@ -17,12 +17,15 @@
 
 #include <array>
 #include <cassert>
+#include <complex>
 #include <ranges>
 #include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
 
+#include "test_macros.h"
+
 namespace my_ns{
 
 struct MyPairLike {
@@ -138,6 +141,49 @@ constexpr bool test() {
       static_assert(!std::is_convertible_v<std::tuple<FromExplicit, FromExplicit>, std::pair<To, To>>);
     }
   }
+
+  // Test construction prohibition of introduced by https://wg21.link/P2255R2.
+#if __has_builtin(__reference_constructs_from_temporary)
+  // Test tuple.
+  {
+    static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, std::tuple<char, long>>);
+    static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, const std::tuple<char, long>&>);
+    static_assert(!std::is_convertible_v<std::tuple<char, long>, std::pair<const int&, const long&>>);
+    static_assert(!std::is_convertible_v<const std::tuple<char, long>&, std::pair<const int&, const long&>>);
+
+    static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, std::tuple<int, short>>);
+    static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, const std::tuple<int, short>&>);
+    static_assert(!std::is_convertible_v<std::tuple<int, short>, std::pair<const int&, const long&>>);
+    static_assert(!std::is_convertible_v<const std::tuple<char, short>&, std::pair<const int&, const long&>>);
+  }
+  // Test array.
+  {
+    static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, std::array<int, 2>>);
+    static_assert(!std::is_constructible_v<std::pair<const long&, const int&>, std::array<int, 2>>);
+    static_assert(!std::is_constructible_v<std::pair<const int&, const long&>, const std::array<int, 2>&>);
+    static_assert(!std::is_constructible_v<std::pair<const long&, const int&>, const std::array<int, 2>&>);
+
+    static_assert(!std::is_convertible_v<std::array<int, 2>, std::pair<const int&, const long&>>);
+    static_assert(!std::is_convertible_v<std::array<int, 2>, std::pair<const long&, const int&>>);
+    static_assert(!std::is_convertible_v<const std::array<int, 2>&, std::pair<const int&, const long&>>);
+    static_assert(!std::is_convertible_v<const std::array<int, 2>&, std::pair<const long&, const int&>>);
+  }
+#  if TEST_STD_VER >= 26
+  // Test complex.
+  {
+    static_assert(!std::is_constructible_v<std::pair<const double&, const float&>, std::complex<float>>);
+    static_assert(!std::is_constructible_v<std::pair<const float&, const double&>, std::complex<float>>);
+    static_assert(!std::is_convertible_v<std::complex<float>, std::pair<const double&, const float&>>);
+    static_assert(!std::is_convertible_v<std::complex<float>, std::pair<const float&, const double&>>);
+
+    static_assert(!std::is_constructible_v<std::pair<const double&, const long double&>, const std::complex<double>&>);
+    static_assert(!std::is_constructible_v<std::pair<const long double&, const double&>, const std::complex<double>&>);
+    static_assert(!std::is_convertible_v<const std::complex<double>&, std::pair<const double&, const long double&>>);
+    static_assert(!std::is_convertible_v<const std::complex<double>&, std::pair<const long double&, const double&>>);
+  }
+#  endif // TEST_STD_VER >= 26
+#endif   // __has_builtin(__reference_constructs_from_temporary)
+
   return true;
 }
 



More information about the libcxx-commits mailing list