[libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple constructor overloads (PR #205379)
Yihan Wang via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jun 30 00:07:23 PDT 2026
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/205379
>From 7f99d7a7375be8f9cfdcf9ca715ecf29ff61380a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:51:43 +0800
Subject: [PATCH 1/8] [libc++] P2255R2: Add deleted tuple constructor overloads
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/include/tuple | 247 ++++++++++++++++--
...5_tuple_ref_binding_diagnostics.verify.cpp | 24 +-
.../ref_constructs_from_temporary.verify.cpp | 74 ++++++
4 files changed, 309 insertions(+), 38 deletions(-)
create mode 100644 libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..7e9f16fbe77ba 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 tuple constructors 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)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 2b32df6bce74a..fabf0a1a43a70 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -633,21 +633,55 @@ public:
_Not<_IsThisTuple<_Up...> >, // extension to allow mis-behaved user constructors
is_constructible<_Tp, _Up>... > {};
- template <class... _Up,
- __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
- int> = 0>
+ template <class... _Up>
+ struct _EnableUTypesCtorBase
+ : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+
+ template <bool, class... _Up>
+ struct _EnableUTypesCtorNoDangling : false_type {};
+
+# if _LIBCPP_STD_VER >= 23
+ template <bool, class... _Up>
+ struct _CtorCreatesDanglingReference : false_type {};
+
+ template <class... _Up>
+ struct _CtorCreatesDanglingReference<true, _Up...>
+ : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+
+ template <class... _Up>
+ struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+
+ template <class... _Up>
+ struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+# else
+ template <class... _Up>
+ struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+# endif // _LIBCPP_STD_VER >= 23
+
+ template <class... _Up>
+ struct _EnableNonDanglingUTypesCtor
+ : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+
+ template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
: __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
- template <class _Alloc,
- class... _Up,
- __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
- int> = 0>
+ template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
: __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
+# if _LIBCPP_STD_VER >= 23
+ template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+ tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+ template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+ tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+# endif // _LIBCPP_STD_VER >= 23
+
// Copy and move constructors (including the allocator_arg_t variants)
tuple(const tuple&) = default;
tuple(tuple&&) = default;
@@ -689,58 +723,134 @@ public:
_Not<is_convertible<_OtherTuple, _Tp> >...,
_Not<is_constructible<_Tp, _OtherTuple> >... > > > {};
- template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+ template <bool, class _OtherTuple>
+ struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
+
+# if _LIBCPP_STD_VER >= 23
+ template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
+ struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
+
+ template <class _OtherTuple, class... _Up>
+ struct _CtorFromUTypesTupleCreatesDanglingReference<
+ true,
+ _OtherTuple,
+ tuple<_Up...>,
+ __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+ : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
+
+ template <class _OtherTuple>
+ struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
+ : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+
+ template <class _OtherTuple>
+ struct _EnableDanglingCtorFromUTypesTuple
+ : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+# else
+ template <class _OtherTuple>
+ struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+# endif // _LIBCPP_STD_VER >= 23
+
+ template <class _OtherTuple>
+ struct _EnableNonDanglingCtorFromUTypesTuple
+ : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+
+ template <class... _Up,
+ __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
: __base_(__from_tuple(), __t) {}
template <class... _Up,
class _Alloc,
- __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+ __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
: __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
# if _LIBCPP_STD_VER >= 23
+ template <class... _Up,
+ __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+ tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
+
+ template <class... _Up,
+ class _Alloc,
+ __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+ tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
+
// tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
- template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
: __base_(__from_tuple(), __t) {}
- template <class _Alloc, class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+
+ template <class _Alloc,
+ class... _Up,
+ enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
+
+ template <class _Alloc,
+ class... _Up,
+ enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
+ tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
# endif // _LIBCPP_STD_VER >= 23
// tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+ template <class... _Up,
+ __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
: __base_(__from_tuple(), std::move(__t)) {}
template <class _Alloc,
class... _Up,
- __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+ __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
: __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
# if _LIBCPP_STD_VER >= 23
+ template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+ tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+ template <class _Alloc,
+ class... _Up,
+ __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+ tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+
// tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ template <class... _Up,
+ enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(const tuple<_Up...>&& __t)
: __base_(__from_tuple(), std::move(__t)) {}
+ template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+ tuple(const tuple<_Up...>&&) = delete;
+
template <class _Alloc,
class... _Up,
- enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
+
+ template <class _Alloc,
+ class... _Up,
+ enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+ tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
# endif // _LIBCPP_STD_VER >= 23
// tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -758,6 +868,34 @@ public:
template <class _Pair>
struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
+ template <bool, class _Pair>
+ struct _EnableCtorFromPairNoDangling : false_type {};
+
+# if _LIBCPP_STD_VER >= 23
+ template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
+ struct _CtorFromPairCreatesDanglingReference : false_type {};
+
+ template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+ struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
+ : _Or<
+ _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+ _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+
+ template <class _Pair>
+ struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+
+ template <class _Pair>
+ struct _EnableDanglingCtorFromPair
+ : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+# else
+ template <class _Pair>
+ struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+# endif // _LIBCPP_STD_VER >= 23
+
+ template <class _Pair>
+ struct _EnableNonDanglingCtorFromPair
+ : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+
template <class _Pair>
struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -771,7 +909,7 @@ public:
template <class _Up1,
class _Up2,
template <class...> class _And = _And,
- __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+ __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -781,35 +919,64 @@ public:
class _Up1,
class _Up2,
template <class...> class _And = _And,
- __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+ __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
: __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
# if _LIBCPP_STD_VER >= 23
+ template <class _Up1,
+ class _Up2,
+ template <class...> class _And = _And,
+ __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+ tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
+ delete;
+
+ template <class _Alloc,
+ class _Up1,
+ class _Up2,
+ template <class...> class _And = _And,
+ __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+ tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
+
// tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
- template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+ template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(pair<_U1, _U2>& __p)
: __base_(__from_tuple(), __p) {}
+ template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+ tuple(pair<_U1, _U2>&) = delete;
+
template <class _Alloc,
class _U1,
class _U2,
- enable_if_t< _EnableCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+ enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
-# endif
+
+ template <class _Alloc,
+ class _U1,
+ class _U2,
+ enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+ tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
+# endif // _LIBCPP_STD_VER >= 23
// tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
template <class _Up1,
class _Up2,
template <class...> class _And = _And,
- __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+ __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -819,27 +986,59 @@ public:
class _Up1,
class _Up2,
template <class...> class _And = _And,
- __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+ __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
: __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
# if _LIBCPP_STD_VER >= 23
+ template <class _Up1,
+ class _Up2,
+ template <class...> class _And = _And,
+ __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+ tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
+
+ template <class _Alloc,
+ class _Up1,
+ class _Up2,
+ template <class...> class _And = _And,
+ __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+ tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
+
// tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
- template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ template <class _U1,
+ class _U2,
+ enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(const pair<_U1, _U2>&& __p)
: __base_(__from_tuple(), std::move(__p)) {}
+ template <class _U1,
+ class _U2,
+ enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+ tuple(const pair<_U1, _U2>&&) = delete;
+
template <class _Alloc,
class _U1,
class _U2,
- enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
+
+ template <class _Alloc,
+ class _U1,
+ class _U2,
+ enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+ tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
# endif // _LIBCPP_STD_VER >= 23
// [tuple.assign]
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a1a80483c4487..a44a38e3e17ed 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++23
// <tuple>
@@ -39,36 +40,33 @@ template <class ...Args>
void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
void f() {
- // Test that we emit our diagnostic from the library.
- // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
-
- // Good news everybody! Clang now diagnoses this for us!
- // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+ // Test that the public constructors are deleted when constructing a reference
+ // element would bind to a temporary.
{
- F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-note 1 {{requested here}}
+ F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
}
{
- std::tuple<int, const std::string&> t(1, "a"); // expected-note 1 {{requested here}}
+ std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
}
{
- F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-note 1 {{requested here}}
+ F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
}
{
ConvertsTo<int&> ct;
- std::tuple<const long&, int> t(ct, 42); // expected-note {{requested here}}
+ std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
}
{
ConvertsTo<int> ct;
- std::tuple<int const&, void*> t(ct, nullptr); // expected-note {{requested here}}
+ std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
}
{
ConvertsTo<Derived> ct;
- std::tuple<Base const&, int> t(ct, 42); // expected-note {{requested here}}
+ std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
}
{
std::allocator<int> alloc;
- std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
- std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
+ std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+ std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
}
}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..c7a5f4e247f3b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <tuple>
+
+// tuple(UTypes&&... u);
+// tuple(tuple<UTypes...>& u);
+// tuple(const tuple<UTypes...>& u);
+// tuple(tuple<UTypes...>&& u);
+// tuple(const tuple<UTypes...>&& u);
+// tuple(pair<U1, U2>& u);
+// tuple(const pair<U1, U2>& u);
+// tuple(pair<U1, U2>&& u);
+// tuple(const pair<U1, U2>&& u);
+//
+// The constructors above are defined as deleted if reference_constructs_from_temporary_v
+// is true for one of the corresponding tuple elements.
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct X {
+ X(int);
+};
+
+static_assert(std::is_constructible_v<std::tuple<const X&>, X&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, int>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&&>);
+
+void test() {
+ std::allocator<int> alloc;
+ std::tuple<int> t(1);
+ const std::tuple<int> ct(1);
+ std::pair<int, int> p(1, 2);
+ const std::pair<int, int> cp(1, 2);
+
+ // expected-error-re@*:* 18 {{call to deleted constructor of 'std::tuple<{{.*}}>'}}
+ std::tuple<const X&> utypes(1);
+ std::tuple<const X&> alloc_utypes(std::allocator_arg, alloc, 1);
+
+ std::tuple<const X&> tuple_lvalue(t);
+ std::tuple<const X&> alloc_tuple_lvalue(std::allocator_arg, alloc, t);
+ std::tuple<const X&> const_tuple_lvalue(ct);
+ std::tuple<const X&> alloc_const_tuple_lvalue(std::allocator_arg, alloc, ct);
+ std::tuple<const X&> tuple_rvalue(std::tuple<int>{1});
+ std::tuple<const X&> alloc_tuple_rvalue(std::allocator_arg, alloc, std::tuple<int>{1});
+ std::tuple<const X&> const_tuple_rvalue(std::move(ct));
+ std::tuple<const X&> alloc_const_tuple_rvalue(std::allocator_arg, alloc, std::move(ct));
+
+ std::tuple<const X&, int> pair_lvalue(p);
+ std::tuple<const X&, int> alloc_pair_lvalue(std::allocator_arg, alloc, p);
+ std::tuple<const X&, int> const_pair_lvalue(cp);
+ std::tuple<const X&, int> alloc_const_pair_lvalue(std::allocator_arg, alloc, cp);
+ std::tuple<const X&, int> pair_rvalue(std::pair<int, int>{1, 2});
+ std::tuple<const X&, int> alloc_pair_rvalue(std::allocator_arg, alloc, std::pair<int, int>{1, 2});
+ std::tuple<const X&, int> const_pair_rvalue(std::move(cp));
+ std::tuple<const X&, int> alloc_const_pair_rvalue(std::allocator_arg, alloc, std::move(cp));
+}
>From 6f3f035857999ee6e2ec274db5b3b1688bf401a2 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:56:58 +0800
Subject: [PATCH 2/8] Format
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/include/tuple | 62 ++++++++-----------
...5_tuple_ref_binding_diagnostics.verify.cpp | 2 +-
2 files changed, 26 insertions(+), 38 deletions(-)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index fabf0a1a43a70..8c6a81e6073ce 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -634,8 +634,7 @@ public:
is_constructible<_Tp, _Up>... > {};
template <class... _Up>
- struct _EnableUTypesCtorBase
- : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+ struct _EnableUTypesCtorBase : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
template <bool, class... _Up>
struct _EnableUTypesCtorNoDangling : false_type {};
@@ -659,8 +658,7 @@ public:
# endif // _LIBCPP_STD_VER >= 23
template <class... _Up>
- struct _EnableNonDanglingUTypesCtor
- : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+ struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
@@ -731,11 +729,10 @@ public:
struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
template <class _OtherTuple, class... _Up>
- struct _CtorFromUTypesTupleCreatesDanglingReference<
- true,
- _OtherTuple,
- tuple<_Up...>,
- __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+ struct _CtorFromUTypesTupleCreatesDanglingReference< true,
+ _OtherTuple,
+ tuple<_Up...>,
+ __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
: _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
template <class _OtherTuple>
@@ -754,8 +751,7 @@ public:
struct _EnableNonDanglingCtorFromUTypesTuple
: _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
- template <class... _Up,
- __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+ template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
: __base_(__from_tuple(), __t) {}
@@ -768,8 +764,7 @@ public:
: __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
# if _LIBCPP_STD_VER >= 23
- template <class... _Up,
- __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+ template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
@@ -803,8 +798,7 @@ public:
# endif // _LIBCPP_STD_VER >= 23
// tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class... _Up,
- __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+ template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
: __base_(__from_tuple(), std::move(__t)) {}
@@ -829,8 +823,7 @@ public:
// tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class... _Up,
- enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(const tuple<_Up...>&& __t)
: __base_(__from_tuple(), std::move(__t)) {}
@@ -877,24 +870,22 @@ public:
template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
- : _Or<
- _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
- _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+ : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+ _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
template <class _Pair>
struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
template <class _Pair>
- struct _EnableDanglingCtorFromPair
- : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+ struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
+ };
# else
template <class _Pair>
struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
# endif // _LIBCPP_STD_VER >= 23
template <class _Pair>
- struct _EnableNonDanglingCtorFromPair
- : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+ struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
template <class _Pair>
struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -908,7 +899,7 @@ public:
template <class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -918,7 +909,7 @@ public:
template <class _Alloc,
class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -928,17 +919,16 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
- tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
- delete;
+ tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
template <class _Alloc,
class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -975,7 +965,7 @@ public:
template <class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -985,7 +975,7 @@ public:
template <class _Alloc,
class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -995,7 +985,7 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1004,7 +994,7 @@ public:
template <class _Alloc,
class _Up1,
class _Up2,
- template <class...> class _And = _And,
+ template <class...> class _And = _And,
__enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1019,9 +1009,7 @@ public:
tuple(const pair<_U1, _U2>&& __p)
: __base_(__from_tuple(), std::move(__p)) {}
- template <class _U1,
- class _U2,
- enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(const pair<_U1, _U2>&&) = delete;
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a44a38e3e17ed..15d032fbb3a7a 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -66,7 +66,7 @@ void f() {
}
{
std::allocator<int> alloc;
- std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+ std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
}
}
>From 03cf6c8b536234b8da2133c7a883232cfbbdfdda Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:41:49 -0700
Subject: [PATCH 3/8] [libc++] Refine the implementation use template variable
and make reference_constructs_from_temporary SFINAE friendly
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
.../reference_constructs_from_temporary.h | 15 +-
libcxx/include/tuple | 240 ++++++++----------
2 files changed, 126 insertions(+), 129 deletions(-)
diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..c18723fb77d81 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,6 +11,7 @@
#include <__config>
#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -30,8 +31,20 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
#endif
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/WMTK5cMa4
+//
+// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+ __reference_constructs_from_temporary(_Tp, _Up);
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8c6a81e6073ce..76ab9da34aabe 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,42 +640,60 @@ public:
struct _EnableUTypesCtorNoDangling : false_type {};
# if _LIBCPP_STD_VER >= 23
- template <bool, class... _Up>
- struct _CtorCreatesDanglingReference : false_type {};
-
template <class... _Up>
- struct _CtorCreatesDanglingReference<true, _Up...>
- : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+ static constexpr bool __has_danglings() {
+ // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
+ // equal length. The discarded branch is not instantiated when the lengths differ.
+ if constexpr (sizeof...(_Up) == sizeof...(_Tp))
+ return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
+ else
+ return false;
+ }
- template <class... _Up>
- struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+ template <class _Tuple,
+ class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+ static inline constexpr bool __has_danglings_from_tuple_like = false;
+
+ template <class _Tuple, size_t... _Idx>
+ static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
+ enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
template <class... _Up>
- struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
-# else
+ static inline constexpr bool __has_danglings_from_utypes =
+ _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+
template <class... _Up>
- struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+ static inline constexpr bool __has_no_danglings_from_utypes =
+ _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
# endif // _LIBCPP_STD_VER >= 23
template <class... _Up>
- struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+ static inline constexpr bool __enable_utypes_constructor_no_dangling() {
+# if _LIBCPP_STD_VER >= 23
+ return __has_no_danglings_from_utypes<_Up...>;
+# else
+ return _EnableUTypesCtorBase<_Up...>::value;
+# endif // _LIBCPP_STD_VER >= 23
+ }
- template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+ template <class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
: __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
- template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+ template <class _Alloc, class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
: __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
# if _LIBCPP_STD_VER >= 23
- template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+ template <class... _Up>
+ requires __has_danglings_from_utypes<_Up...>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
- template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+ template <class _Alloc, class... _Up>
+ requires __has_danglings_from_utypes<_Up...>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
# endif // _LIBCPP_STD_VER >= 23
@@ -725,123 +743,116 @@ public:
struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
# if _LIBCPP_STD_VER >= 23
- template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
- struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
-
- template <class _OtherTuple, class... _Up>
- struct _CtorFromUTypesTupleCreatesDanglingReference< true,
- _OtherTuple,
- tuple<_Up...>,
- __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
- : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
-
template <class _OtherTuple>
- struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
- : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+ static inline constexpr bool __has_danglings_from_utypes_tuple =
+ _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
template <class _OtherTuple>
- struct _EnableDanglingCtorFromUTypesTuple
- : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
-# else
- template <class _OtherTuple>
- struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+ static inline constexpr bool __has_no_danglings_from_utypes_tuple =
+ _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
# endif // _LIBCPP_STD_VER >= 23
template <class _OtherTuple>
- struct _EnableNonDanglingCtorFromUTypesTuple
- : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+ static inline constexpr bool __enable_utypes_tuple_constructor_no_dangling() {
+# if _LIBCPP_STD_VER >= 23
+ return __has_no_danglings_from_utypes_tuple<_OtherTuple>;
+# else
+ return _EnableCtorFromUTypesTuple<_OtherTuple>::value;
+# endif // _LIBCPP_STD_VER >= 23
+ }
- template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+ template <class... _Up,
+ __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
: __base_(__from_tuple(), __t) {}
template <class... _Up,
class _Alloc,
- __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+ __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
: __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
# if _LIBCPP_STD_VER >= 23
- template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+ template <class... _Up>
+ requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
- template <class... _Up,
- class _Alloc,
- __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+ template <class... _Up, class _Alloc>
+ requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
// tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
- template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ template <class... _Up>
+ requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
: __base_(__from_tuple(), __t) {}
- template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ template <class... _Up>
+ requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
- template <class _Alloc,
- class... _Up,
- enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ template <class _Alloc, class... _Up>
+ requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
- template <class _Alloc,
- class... _Up,
- enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ template <class _Alloc, class... _Up>
+ requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
# endif // _LIBCPP_STD_VER >= 23
// tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+ template <class... _Up, __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
: __base_(__from_tuple(), std::move(__t)) {}
template <class _Alloc,
class... _Up,
- __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+ __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
: __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
# if _LIBCPP_STD_VER >= 23
- template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+ template <class... _Up>
+ requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
- template <class _Alloc,
- class... _Up,
- __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+ template <class _Alloc, class... _Up>
+ requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
// tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ template <class... _Up>
+ requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(const tuple<_Up...>&& __t)
: __base_(__from_tuple(), std::move(__t)) {}
- template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ template <class... _Up>
+ requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(const tuple<_Up...>&&) = delete;
- template <class _Alloc,
- class... _Up,
- enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ template <class _Alloc, class... _Up>
+ requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
- template <class _Alloc,
- class... _Up,
- enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ template <class _Alloc, class... _Up>
+ requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
# endif // _LIBCPP_STD_VER >= 23
@@ -861,31 +872,24 @@ public:
template <class _Pair>
struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
- template <bool, class _Pair>
- struct _EnableCtorFromPairNoDangling : false_type {};
-
# if _LIBCPP_STD_VER >= 23
- template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
- struct _CtorFromPairCreatesDanglingReference : false_type {};
-
- template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
- struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
- : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
- _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
-
template <class _Pair>
- struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+ static inline constexpr bool __has_danglings_from_utypes_pair =
+ _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
template <class _Pair>
- struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
- };
-# else
- template <class _Pair>
- struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+ static inline constexpr bool __has_no_danglings_from_utypes_pair =
+ _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
# endif // _LIBCPP_STD_VER >= 23
template <class _Pair>
- struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+ static inline constexpr bool __enable_utypes_pair_constructor_no_dangling() {
+# if _LIBCPP_STD_VER >= 23
+ return _EnableCtorFromPair<_Pair>::value && __has_no_danglings_from_utypes_pair<_Pair>;
+# else
+ return _EnableCtorFromPair<_Pair>::value;
+# endif // _LIBCPP_STD_VER >= 23
+ }
template <class _Pair>
struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -899,8 +903,7 @@ public:
template <class _Up1,
class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+ __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -909,54 +912,46 @@ public:
template <class _Alloc,
class _Up1,
class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+ __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
: __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
# if _LIBCPP_STD_VER >= 23
- template <class _Up1,
- class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+ template <class _Up1, class _Up2>
+ requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
- template <class _Alloc,
- class _Up1,
- class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+ template <class _Alloc, class _Up1, class _Up2>
+ requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
// tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
- template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+ template <class _U1, class _U2>
+ requires __has_no_danglings_from_utypes_pair<pair<_U1, _U2>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(pair<_U1, _U2>& __p)
: __base_(__from_tuple(), __p) {}
- template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+ template <class _U1, class _U2>
+ requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(pair<_U1, _U2>&) = delete;
- template <class _Alloc,
- class _U1,
- class _U2,
- enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+ template <class _Alloc, class _U1, class _U2>
+ requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
- template <class _Alloc,
- class _U1,
- class _U2,
- enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+ template <class _Alloc, class _U1, class _U2>
+ requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
# endif // _LIBCPP_STD_VER >= 23
@@ -965,8 +960,7 @@ public:
template <class _Up1,
class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+ __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -975,56 +969,46 @@ public:
template <class _Alloc,
class _Up1,
class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+ __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
: __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
# if _LIBCPP_STD_VER >= 23
- template <class _Up1,
- class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+ template <class _Up1, class _Up2>
+ requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
- template <class _Alloc,
- class _Up1,
- class _Up2,
- template <class...> class _And = _And,
- __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+ template <class _Alloc, class _Up1, class _Up2>
+ requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
// tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
- template <class _U1,
- class _U2,
- enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ template <class _U1, class _U2>
+ requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(const pair<_U1, _U2>&& __p)
: __base_(__from_tuple(), std::move(__p)) {}
- template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ template <class _U1, class _U2>
+ requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(const pair<_U1, _U2>&&) = delete;
- template <class _Alloc,
- class _U1,
- class _U2,
- enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ template <class _Alloc, class _U1, class _U2>
+ requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
: __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
- template <class _Alloc,
- class _U1,
- class _U2,
- enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ template <class _Alloc, class _U1, class _U2>
+ requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
# endif // _LIBCPP_STD_VER >= 23
>From 28a00ab051c72c567a6c5082efb9dcb6963f3a68 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:46:03 -0700
Subject: [PATCH 4/8] [libc++] Enable test for all language modes
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
.../tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 15d032fbb3a7a..0e9992194f03c 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
-// REQUIRES: std-at-least-c++23
// <tuple>
>From c5d0b0e748a328c6041d0aaa8009663772e1e89a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:06:24 -0700
Subject: [PATCH 5/8] Remove unnecessary for deleted ctors
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/include/tuple | 46 +++++++++++++++++---------------------------
1 file changed, 18 insertions(+), 28 deletions(-)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 76ab9da34aabe..c368fe3ab6aea 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -689,13 +689,12 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class... _Up>
requires __has_danglings_from_utypes<_Up...>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+ explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
template <class _Alloc, class... _Up>
requires __has_danglings_from_utypes<_Up...>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
- tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+ explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
# endif // _LIBCPP_STD_VER >= 23
// Copy and move constructors (including the allocator_arg_t variants)
@@ -777,12 +776,12 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class... _Up>
requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+ explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
template <class... _Up, class _Alloc>
requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+ explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
// tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
@@ -794,7 +793,7 @@ public:
template <class... _Up>
requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+ explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
template <class _Alloc, class... _Up>
requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
@@ -804,8 +803,7 @@ public:
template <class _Alloc, class... _Up>
requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
- tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
+ explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
# endif // _LIBCPP_STD_VER >= 23
// tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
@@ -824,13 +822,12 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class... _Up>
requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+ explicit(!_And<is_convertible<_Up, _Tp>...>::value)
tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
template <class _Alloc, class... _Up>
requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
- tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+ explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
// tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
@@ -842,8 +839,7 @@ public:
template <class... _Up>
requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
- tuple(const tuple<_Up...>&&) = delete;
+ explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value) tuple(const tuple<_Up...>&&) = delete;
template <class _Alloc, class... _Up>
requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
@@ -853,7 +849,7 @@ public:
template <class _Alloc, class... _Up>
requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+ explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
# endif // _LIBCPP_STD_VER >= 23
@@ -921,14 +917,12 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _Up1, class _Up2>
requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+ explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
template <class _Alloc, class _Up1, class _Up2>
requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+ explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
// tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -941,8 +935,7 @@ public:
template <class _U1, class _U2>
requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
- tuple(pair<_U1, _U2>&) = delete;
+ explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value) tuple(pair<_U1, _U2>&) = delete;
template <class _Alloc, class _U1, class _U2>
requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
@@ -952,7 +945,7 @@ public:
template <class _Alloc, class _U1, class _U2>
requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+ explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
# endif // _LIBCPP_STD_VER >= 23
@@ -978,14 +971,12 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _Up1, class _Up2>
requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+ explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
template <class _Alloc, class _Up1, class _Up2>
requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+ explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
// tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
@@ -998,8 +989,7 @@ public:
template <class _U1, class _U2>
requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
- tuple(const pair<_U1, _U2>&&) = delete;
+ explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value) tuple(const pair<_U1, _U2>&&) = delete;
template <class _Alloc, class _U1, class _U2>
requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
@@ -1009,7 +999,7 @@ public:
template <class _Alloc, class _U1, class _U2>
requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+ explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
# endif // _LIBCPP_STD_VER >= 23
>From 12b049f103620f05a538a9c395d2dbc95bbf2020 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:13:12 -0700
Subject: [PATCH 6/8] Format
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/include/tuple | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index c368fe3ab6aea..dd462132e4f31 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -650,13 +650,14 @@ public:
return false;
}
- template <class _Tuple,
- class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+ template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
static inline constexpr bool __has_danglings_from_tuple_like = false;
template <class _Tuple, size_t... _Idx>
- static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
- enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+ static inline constexpr bool __has_danglings_from_tuple_like<
+ _Tuple,
+ index_sequence<_Idx...>,
+ enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
template <class... _Up>
static inline constexpr bool __has_danglings_from_utypes =
>From df278e7b6f50fc7b81e516084a398ee3d44918d8 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:20:32 -0700
Subject: [PATCH 7/8] Remove unnecessary comments
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/include/tuple | 2 --
1 file changed, 2 deletions(-)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index dd462132e4f31..1b88307ec5aa2 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -642,8 +642,6 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class... _Up>
static constexpr bool __has_danglings() {
- // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
- // equal length. The discarded branch is not instantiated when the lengths differ.
if constexpr (sizeof...(_Up) == sizeof...(_Tp))
return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
else
>From 887e5af402386ef92a56ae7c880fd7d7fec2ea49 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:06:59 -0700
Subject: [PATCH 8/8] [libc++] Fix hard error in deleted dangling ctor
constraints
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/include/tuple | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 22641af8f494c..0a95aa635663d 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,13 +640,25 @@ public:
index_sequence<_Idx...>,
enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+ template <bool __dangles, class _CtorEnabled>
+ static inline constexpr bool __check_danglings_from_constructor = false;
+
+ template <class _CtorEnabled>
+ static inline constexpr bool __check_danglings_from_constructor<true, _CtorEnabled> = _CtorEnabled::value;
+
+ template <bool __dangles, class _CtorEnabled>
+ static inline constexpr bool __check_no_danglings_from_constructor = _CtorEnabled::value;
+
+ template <class _CtorEnabled>
+ static inline constexpr bool __check_no_danglings_from_constructor<true, _CtorEnabled> = false;
+
template <class... _Up>
static inline constexpr bool __has_danglings_from_utypes =
- _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+ __check_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
template <class... _Up>
static inline constexpr bool __has_no_danglings_from_utypes =
- _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
+ __check_no_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
# endif // _LIBCPP_STD_VER >= 23
template <class... _Up>
@@ -726,11 +738,13 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _OtherTuple>
static inline constexpr bool __has_danglings_from_utypes_tuple =
- _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
+ __check_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+ _EnableCtorFromUTypesTuple<_OtherTuple> >;
template <class _OtherTuple>
static inline constexpr bool __has_no_danglings_from_utypes_tuple =
- _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
+ __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+ _EnableCtorFromUTypesTuple<_OtherTuple> >;
# endif // _LIBCPP_STD_VER >= 23
template <class _OtherTuple>
@@ -853,11 +867,11 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _Pair>
static inline constexpr bool __has_danglings_from_utypes_pair =
- _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
+ __check_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
template <class _Pair>
static inline constexpr bool __has_no_danglings_from_utypes_pair =
- _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
+ __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
# endif // _LIBCPP_STD_VER >= 23
template <class _Pair>
More information about the libcxx-commits
mailing list