On Thu, 11 Jan 2024, Jonathan Wakely wrote:

> I'd like to commit this to trunk for GCC 14. Please take a look.
> 
> -- >8 --
> 
> This is the last part of PR libstdc++/108822 implementing P2255R2, which
> makes it ill-formed to create a std::tuple that would bind a reference
> to a temporary.
> 
> The dangling checks are implemented as deleted constructors for C++20
> and higher, and as Debug Mode static assertions in the constructor body
> for older standards. This is similar to the r13-6084-g916ce577ad109b
> changes for std::pair.
> 
> As part of this change, I've reimplemented most of std::tuple for C++20,
> making use of concepts to replace the enable_if constraints, and using
> conditional explicit to avoid duplicating most constructors. We could
> use conditional explicit for the C++11 implementation too (with pragmas
> to disables the -Wc++17-extensions warnings), but that should be done as
> a stage 1 change for GCC 15 rather than now.
> 
> The partial specialization for std::tuple<T1, T2> is no longer used for
> C++20 (or more precisely, for a C++20 compiler that supports concepts
> and conditional explicit). The additional constructors and assignment
> operators that take std::pair arguments have been added to the C++20
> implementation of the primary template, with sizeof...(_Elements)==2
> constraints. This avoids reimplementing all the other constructors in
> the std::tuple<T1, T2> partial specialization to use concepts. This way
> we avoid four implementations of every constructor and only have three!
> (The primary template has an implementation of each constructor for
> C++11 and another for C++20, and the tuple<T1,T2> specialization has an
> implementation of each for C++11, so that's three for each constructor.)
> 
> In order to make the constraints more efficient on the C++20 version of
> the default constructor I've also added a variable template for the
> __is_implicitly_default_constructible trait, implemented using concepts.
> 
> libstdc++-v3/ChangeLog:
> 
>       PR libstdc++/108822
>       * include/std/tuple (tuple): Add checks for dangling references.
>       Reimplement constraints and constant expressions using C++20
>       features.
>       * include/std/type_traits [C++20]
>       (__is_implicitly_default_constructible_v): Define.
>       (__is_implicitly_default_constructible): Use variable template.
>       * testsuite/20_util/tuple/dangling_ref.cc: New test.
> ---
>  libstdc++-v3/include/std/tuple                | 1021 ++++++++++++-----
>  libstdc++-v3/include/std/type_traits          |   11 +
>  .../testsuite/20_util/tuple/dangling_ref.cc   |  105 ++
>  3 files changed, 841 insertions(+), 296 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc
> 
> diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
> index 50e11843757..cd05b638923 100644
> --- a/libstdc++-v3/include/std/tuple
> +++ b/libstdc++-v3/include/std/tuple
> @@ -752,11 +752,467 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    template<typename... _Elements>
>      class tuple : public _Tuple_impl<0, _Elements...>
>      {
> -      typedef _Tuple_impl<0, _Elements...> _Inherited;
> +      using _Inherited = _Tuple_impl<0, _Elements...>;
>  
>        template<bool _Cond>
>       using _TCC = _TupleConstraints<_Cond, _Elements...>;

I guess this should be moved into the #else branch if it's not used in
the new impl.

>  
> +#if __cpp_concepts && __cpp_conditional_explicit // >= C++20
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __constructible()
> +     {
> +       if constexpr (sizeof...(_UTypes) == sizeof...(_Elements))
> +         return (is_constructible_v<_Elements, _UTypes> && ...);

IIUC this (and all the other new constraints) won't short-circuit like
the old versions do :/ Not sure how much that matters?

> +       else
> +         return false;
> +     }
> +
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __nothrow_constructible()
> +     {
> +       if constexpr (sizeof...(_UTypes) == sizeof...(_Elements))
> +         return (is_nothrow_constructible_v<_Elements, _UTypes> && ...);
> +       else
> +         return false;
> +     }
> +
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __convertible()
> +     {
> +       if constexpr (sizeof...(_UTypes) == sizeof...(_Elements))
> +         return (is_convertible_v<_UTypes, _Elements> && ...);
> +       else
> +         return false;
> +     }
> +
> +      // _GLIBCXX_RESOLVE_LIB_DEFECTS
> +      // 3121. tuple constructor constraints for UTypes&&... overloads
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __disambiguating_constraint()
> +     {
> +       if constexpr (sizeof...(_Elements) != sizeof...(_UTypes))
> +         return false;
> +       else if constexpr (sizeof...(_Elements) == 1)
> +         {
> +           using _U0 = typename _Nth_type<0, _UTypes...>::type;
> +           return !is_same_v<remove_cvref_t<_U0>, tuple>;
> +         }
> +       else if constexpr (sizeof...(_Elements) < 4)
> +         {
> +           using _U0 = typename _Nth_type<0, _UTypes...>::type;
> +           if constexpr (!is_same_v<remove_cvref_t<_U0>, allocator_arg_t>)
> +             return true;
> +           else
> +             {
> +               using _T0 = typename _Nth_type<0, _Elements...>::type;
> +               return is_same_v<remove_cvref_t<_T0>, allocator_arg_t>;
> +             }
> +         }
> +       return true;
> +     }
> +
> +      // Return true iff sizeof...(Types) == 1 && tuple_size_v<TUPLE> == 1
> +      // and the single element in Types can be initialized from TUPLE,
> +      // or is the same type as tuple_element_t<0, TUPLE>.
> +      template<typename _Tuple>
> +     static consteval bool
> +     __use_other_ctor()
> +     {
> +       if constexpr (sizeof...(_Elements) != 1)
> +         return false;
> +       else if constexpr (is_same_v<remove_cvref_t<_Tuple>, tuple>)
> +         return true; // Should use a copy/move constructor instead.
> +       else
> +         {
> +           using _Tp = typename _Nth_type<0, _Elements...>::type;
> +           if constexpr (is_convertible_v<_Tuple, _Tp>)
> +             return true;
> +           else if constexpr (is_constructible_v<_Tp, _Tuple>)
> +             return true;
> +         }
> +       return false;
> +     }
> +
> +      template<typename... _Up>
> +     static consteval bool
> +     __dangles()
> +     {
> +#if __has_builtin(__reference_constructs_from_temporary)
> +       return (__reference_constructs_from_temporary(_Elements, _Up&&)
> +                 || ...);
> +#else
> +       return false;
> +#endif
> +     }
> +
> +    public:
> +      constexpr
> +      explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...))
> +      tuple()
> +      noexcept((is_nothrow_default_constructible_v<_Elements> && ...))
> +      requires (is_default_constructible_v<_Elements> && ...)
> +      : _Inherited()
> +      { }
> +
> +      constexpr explicit(!__convertible<const _Elements&...>())
> +      tuple(const _Elements&... __elements)
> +      noexcept(__nothrow_constructible<const _Elements&...>())
> +      requires (__constructible<const _Elements&...>())
> +      : _Inherited(__elements...)
> +      { }
> +
> +      template<typename... _UTypes>
> +     requires (__disambiguating_constraint<_UTypes...>())
> +       && (__constructible<_UTypes...>())
> +       && (!__dangles<_UTypes...>())
> +     constexpr explicit(!__convertible<_UTypes...>())
> +     tuple(_UTypes&&... __u)
> +     noexcept(__nothrow_constructible<_UTypes...>())
> +     : _Inherited(std::forward<_UTypes>(__u)...)
> +     { }
> +
> +      template<typename... _UTypes>
> +     requires (__disambiguating_constraint<_UTypes...>())
> +       && (__constructible<_UTypes...>())
> +       && (__dangles<_UTypes...>())
> +     tuple(_UTypes&&...) = delete;
> +
> +      constexpr tuple(const tuple&) = default;
> +
> +      constexpr tuple(tuple&&) = default;
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<const _UTypes&...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>&>())
> +       && (!__dangles<const _UTypes&...>())
> +     constexpr explicit(!__convertible<const _UTypes&...>())
> +     tuple(const tuple<_UTypes...>& __u)
> +     noexcept(__nothrow_constructible<const _UTypes&...>())
> +     : _Inherited(static_cast<const _Tuple_impl<0, _UTypes...>&>(__u))
> +     { }
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<const _UTypes&...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>&>())
> +       && (__dangles<const _UTypes&...>())
> +     tuple(const tuple<_UTypes...>&) = delete;
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<_UTypes...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>>())
> +       && (!__dangles<_UTypes...>())
> +     constexpr explicit(!__convertible<_UTypes...>())
> +     tuple(tuple<_UTypes...>&& __u)
> +     noexcept(__nothrow_constructible<_UTypes...>())
> +     : _Inherited(static_cast<_Tuple_impl<0, _UTypes...>&&>(__u))
> +     { }
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<_UTypes...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>>())
> +       && (__dangles<_UTypes...>())
> +     tuple(tuple<_UTypes...>&&) = delete;
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      template<typename... _UTypes>
> +     requires (__constructible<_UTypes&...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>&>())
> +       && (!__dangles<_UTypes&...>())
> +     constexpr explicit(!__convertible<_UTypes&...>())
> +     tuple(tuple<_UTypes...>& __u)
> +     noexcept(__nothrow_constructible<_UTypes&...>())
> +     : _Inherited(static_cast<_Tuple_impl<0, _UTypes...>&>(__u))
> +     { }
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<_UTypes&...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>&>())
> +       && (__dangles<_UTypes&...>())
> +     tuple(tuple<_UTypes...>&) = delete;
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<const _UTypes...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>>())
> +       && (!__dangles<const _UTypes...>())
> +     constexpr explicit(!__convertible<const _UTypes...>())
> +     tuple(const tuple<_UTypes...>&& __u)
> +     noexcept(__nothrow_constructible<const _UTypes...>())
> +     : _Inherited(static_cast<const _Tuple_impl<0, _UTypes...>&&>(__u))
> +     { }
> +
> +      template<typename... _UTypes>
> +     requires (__constructible<const _UTypes...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>>())
> +       && (__dangles<const _UTypes...>())
> +     tuple(const tuple<_UTypes...>&&) = delete;
> +#endif // C++23
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1&, const _U2&>())
> +       && (!__dangles<const _U1&, const _U2&>())
> +     constexpr explicit(!__convertible<const _U1&, const _U2&>())
> +     tuple(const pair<_U1, _U2>& __u)
> +     noexcept(__nothrow_constructible<const _U1&, const _U2&>())
> +     : _Inherited(__u.first, __u.second)
> +     { }
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1&, const _U2&>())
> +       && (__dangles<const _U1&, const _U2&>())
> +     tuple(const pair<_U1, _U2>&) = delete;
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1, _U2>())
> +       && (!__dangles<_U1, _U2>())
> +     constexpr explicit(!__convertible<_U1, _U2>())
> +     tuple(pair<_U1, _U2>&& __u)
> +     noexcept(__nothrow_constructible<_U1, _U2>())
> +     : _Inherited(std::forward<_U1>(__u.first),
> +                  std::forward<_U2>(__u.second))
> +     { }
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1, _U2>())
> +       && (__dangles<_U1, _U2>())
> +     tuple(pair<_U1, _U2>&&) = delete;
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1&, _U2&>())
> +       && (!__dangles<_U1&, _U2&>())
> +     constexpr explicit(!__convertible<_U1&, _U2&>())
> +     tuple(pair<_U1, _U2>& __u)
> +     noexcept(__nothrow_constructible<_U1&, _U2&>())
> +     : _Inherited(__u.first, __u.second)
> +     { }
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1&, _U2&>())
> +       && (__dangles<_U1&, _U2&>())
> +     tuple(pair<_U1, _U2>&) = delete;
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1, const _U2>())
> +       && (!__dangles<const _U1, const _U2>())
> +     constexpr explicit(!__convertible<const _U1, const _U2>())
> +     tuple(const pair<_U1, _U2>&& __u)
> +     noexcept(__nothrow_constructible<const _U1, const _U2>())
> +     : _Inherited(std::forward<const _U1>(__u.first),
> +                  std::forward<const _U2>(__u.second))
> +     { }
> +
> +      template<typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1, const _U2>())
> +       && (__dangles<const _U1, const _U2>())
> +     tuple(const pair<_U1, _U2>&&) = delete;
> +#endif // C++23
> +
> +#if 0 && __cpp_lib_tuple_like // >= C++23
> +      template<__tuple_like _UTuple>
> +     constexpr explicit(...)
> +     tuple(_UTuple&& __u);
> +#endif // C++23
> +
> +      // Allocator-extended constructors.
> +
> +      template<typename _Alloc>
> +     constexpr
> +     explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...))
> +     tuple(allocator_arg_t __tag, const _Alloc& __a)
> +     requires (is_default_constructible_v<_Elements> && ...)
> +     : _Inherited(__tag, __a)
> +     { }
> +
> +      template<typename _Alloc>
> +     constexpr explicit(!__convertible<const _Elements&...>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a,
> +           const _Elements&... __elements)
> +     requires (__constructible<const _Elements&...>())
> +     : _Inherited(__tag, __a, __elements...)
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__disambiguating_constraint<_UTypes...>())
> +       && (__constructible<_UTypes...>())
> +       && (!__dangles<_UTypes...>())
> +     constexpr explicit(!__convertible<_UTypes...>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, _UTypes&&... __u)
> +     : _Inherited(__tag, __a, std::forward<_UTypes>(__u)...)
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__disambiguating_constraint<_UTypes...>())
> +       && (__constructible<_UTypes...>())
> +       && (__dangles<_UTypes...>())
> +     tuple(allocator_arg_t, const _Alloc&, _UTypes&&...) = delete;
> +
> +      template<typename _Alloc>
> +     constexpr
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple& __u)
> +     : _Inherited(__tag, __a, static_cast<const _Inherited&>(__u))
> +     { }
> +
> +      template<typename _Alloc>
> +     requires (__constructible<_Elements...>())
> +     constexpr
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, tuple&& __u)
> +     : _Inherited(__tag, __a, static_cast<_Inherited&&>(__u))
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<const _UTypes&...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>&>())
> +       && (!__dangles<const _UTypes&...>())
> +     constexpr explicit(!__convertible<const _UTypes&...>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a,
> +           const tuple<_UTypes...>& __u)
> +     : _Inherited(__tag, __a,
> +                  static_cast<const _Tuple_impl<0, _UTypes...>&>(__u))
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<const _UTypes&...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>&>())
> +       && (__dangles<const _UTypes&...>())
> +     tuple(allocator_arg_t, const _Alloc&, const tuple<_UTypes...>&) = 
> delete;
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<_UTypes...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>>())
> +       && (!__dangles<_UTypes...>())
> +     constexpr explicit(!__use_other_ctor<tuple<_UTypes...>>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_UTypes...>&& __u)
> +     : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _UTypes...>&&>(__u))
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<_UTypes...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>>())
> +       && (__dangles<_UTypes...>())
> +     tuple(allocator_arg_t, const _Alloc&, tuple<_UTypes...>&&) = delete;
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<_UTypes&...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>&>())
> +       && (!__dangles<_UTypes&...>())
> +     constexpr explicit(!__convertible<_UTypes&...>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_UTypes...>& __u)
> +     : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _UTypes...>&>(__u))
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<_UTypes&...>())
> +       && (!__use_other_ctor<tuple<_UTypes...>&>())
> +       && (__dangles<_UTypes&...>())
> +     tuple(allocator_arg_t, const _Alloc&, tuple<_UTypes...>&) = delete;
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<const _UTypes...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>>())
> +       && (!__dangles<const _UTypes...>())
> +     constexpr explicit(!__convertible<const _UTypes...>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a,
> +           const tuple<_UTypes...>&& __u)
> +     : _Inherited(__tag, __a,
> +                  static_cast<const _Tuple_impl<0, _UTypes...>&&>(__u))
> +     { }
> +
> +      template<typename _Alloc, typename... _UTypes>
> +     requires (__constructible<const _UTypes...>())
> +       && (!__use_other_ctor<const tuple<_UTypes...>>())
> +       && (__dangles<const _UTypes...>())
> +     tuple(allocator_arg_t, const _Alloc&, const tuple<_UTypes...>&&) = 
> delete;
> +#endif // C++23
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1&, const _U2&>())
> +       && (!__dangles<const _U1&, const _U2&>())
> +     constexpr explicit(!__convertible<const _U1&, const _U2&>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a,
> +           const pair<_U1, _U2>& __u)
> +     noexcept(__nothrow_constructible<const _U1&, const _U2&>())
> +     : _Inherited(__tag, __a, __u.first, __u.second)
> +     { }
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1&, const _U2&>())
> +       && (__dangles<const _U1&, const _U2&>())
> +     tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&) = delete;
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1, _U2>())
> +       && (!__dangles<_U1, _U2>())
> +     constexpr explicit(!__convertible<_U1, _U2>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __u)
> +     noexcept(__nothrow_constructible<_U1, _U2>())
> +     : _Inherited(__tag, __a, std::move(__u.first), std::move(__u.second))
> +     { }
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1, _U2>())
> +       && (__dangles<_U1, _U2>())
> +     tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&&) = delete;
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1&, _U2&>())
> +       && (!__dangles<_U1&, _U2&>())
> +     constexpr explicit(!__convertible<_U1&, _U2&>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>& __u)
> +     noexcept(__nothrow_constructible<_U1&, _U2&>())
> +     : _Inherited(__tag, __a, __u.first, __u.second)
> +     { }
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<_U1&, _U2&>())
> +       && (__dangles<_U1&, _U2&>())
> +     tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1, const _U2>())
> +       && (!__dangles<const _U1, const _U2>())
> +     constexpr explicit(!__convertible<const _U1, const _U2>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a,
> +     const pair<_U1, _U2>&& __u)
> +     noexcept(__nothrow_constructible<const _U1, const _U2>())
> +     : _Inherited(__tag, __a, std::move(__u.first), std::move(__u.second))
> +     { }
> +
> +      template<typename _Alloc, typename _U1, typename _U2>
> +     requires (sizeof...(_Elements) == 2)
> +       && (__constructible<const _U1, const _U2>())
> +       && (__dangles<const _U1, const _U2>())
> +     tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
> +#endif // C++23
> +
> +#if 0 && __cpp_lib_tuple_like // >= C++23
> +      template<typename _Alloc, __tuple_like _UTuple>
> +     constexpr explicit(...)
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u);
> +#endif // C++23
> +
> +#else // !(concepts && conditional_explicit)
> +
>        // Constraint for non-explicit default constructor
>        template<bool _Dummy>
>       using _ImplicitDefaultCtor = __enable_if_t<
> @@ -850,15 +1306,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       static constexpr bool __use_other_ctor()
>       { return _UseOtherCtor<_Tuple>::value; }
>  
> -#if __cplusplus > 202002L
> -      template<typename... _Args>
> -     static constexpr bool __constructible
> -       = _TCC<true>::template __constructible<_Args...>::value;
> -
> -      template<typename... _Args>
> -     static constexpr bool __convertible
> -       = _TCC<true>::template __convertible<_Args...>::value;
> -#endif // C++23
> +      /// @cond undocumented
> +#undef __glibcxx_no_dangling_refs
> +#if __has_builtin(__reference_constructs_from_temporary) \
> +      && defined _GLIBCXX_DEBUG
> +      // Error if construction from U... would create a dangling ref.
> +# if __cpp_fold_expressions
> +#  define __glibcxx_dangling_refs(U) \
> +  (__reference_constructs_from_temporary(_Elements, U) && ...)
> +# else
> +#  define __glibcxx_dangling_refs(U) \
> +  __or_<__bool_constant<__reference_constructs_from_temporary(_Elements, U) \
> +       >...>::value
> +# endif
> +# define __glibcxx_no_dangling_refs(U) \
> +  static_assert(!__glibcxx_dangling_refs(U), \
> +             "std::tuple constructor creates a dangling reference")
> +#else
> +# define __glibcxx_no_dangling_refs(U)
> +#endif
> +      /// @endcond
>  
>      public:
>        template<typename _Dummy = void,
> @@ -895,7 +1362,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       constexpr
>       tuple(_UElements&&... __elements)
>       noexcept(__nothrow_constructible<_UElements...>())
> -     : _Inherited(std::forward<_UElements>(__elements)...) { }
> +     : _Inherited(std::forward<_UElements>(__elements)...)
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        template<typename... _UElements,
>              bool _Valid = __valid_args<_UElements...>(),
> @@ -903,7 +1371,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       explicit constexpr
>       tuple(_UElements&&... __elements)
>       noexcept(__nothrow_constructible<_UElements...>())
> -     : _Inherited(std::forward<_UElements>(__elements)...) { }
> +     : _Inherited(std::forward<_UElements>(__elements)...)
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        constexpr tuple(const tuple&) = default;
>  
> @@ -917,7 +1386,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(const tuple<_UElements...>& __in)
>       noexcept(__nothrow_constructible<const _UElements&...>())
>       : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
> -     { }
> +     { __glibcxx_no_dangling_refs(const _UElements&); }
>  
>        template<typename... _UElements,
>              bool _Valid = (sizeof...(_Elements) == sizeof...(_UElements))
> @@ -927,7 +1396,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(const tuple<_UElements...>& __in)
>       noexcept(__nothrow_constructible<const _UElements&...>())
>       : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
> -     { }
> +     { __glibcxx_no_dangling_refs(const _UElements&); }
>  
>        template<typename... _UElements,
>              bool _Valid = (sizeof...(_Elements) == sizeof...(_UElements))
> @@ -936,7 +1405,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       constexpr
>       tuple(tuple<_UElements...>&& __in)
>       noexcept(__nothrow_constructible<_UElements...>())
> -     : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
> +     : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        template<typename... _UElements,
>              bool _Valid = (sizeof...(_Elements) == sizeof...(_UElements))
> @@ -945,30 +1415,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       explicit constexpr
>       tuple(tuple<_UElements...>&& __in)
>       noexcept(__nothrow_constructible<_UElements...>())
> -     : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
> -
> -#if __cplusplus > 202002L
> -      template<typename... _UElements>
> -     requires (sizeof...(_Elements) == sizeof...(_UElements))
> -       && (!__use_other_ctor<tuple<_UElements...>&>())
> -       && __constructible<_UElements&...>
> -     explicit(!__convertible<_UElements&...>)
> -     constexpr
> -     tuple(tuple<_UElements...>& __in)
> -     noexcept(__nothrow_constructible<_UElements&...>())
> -     : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&>(__in))
> -     { }
> -
> -      template<typename... _UElements>
> -     requires (sizeof...(_Elements) == sizeof...(_UElements))
> -       && (!__use_other_ctor<const tuple<_UElements...>&&>())
> -       && __constructible<const _UElements...>
> -     explicit(!__convertible<const _UElements...>)
> -     constexpr
> -     tuple(const tuple<_UElements...>&& __in)
> -     noexcept(__nothrow_constructible<const _UElements...>())
> -     : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&&>(__in)) 
> { }
> -#endif // C++23
> +     : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        // Allocator-extended constructors.
>  
> @@ -1000,7 +1448,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             _UElements&&... __elements)
>       : _Inherited(__tag, __a, std::forward<_UElements>(__elements)...)
> -     { }
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        template<typename _Alloc, typename... _UElements,
>                bool _Valid = __valid_args<_UElements...>(),
> @@ -1010,7 +1458,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             _UElements&&... __elements)
>       : _Inherited(__tag, __a, std::forward<_UElements>(__elements)...)
> -     { }
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        template<typename _Alloc>
>       _GLIBCXX20_CONSTEXPR
> @@ -1030,8 +1478,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             const tuple<_UElements...>& __in)
>       : _Inherited(__tag, __a,
> -                  static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
> -     { }
> +                  static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
> +     { __glibcxx_no_dangling_refs(const _UElements&); }
>  
>        template<typename _Alloc, typename... _UElements,
>              bool _Valid = (sizeof...(_Elements) == sizeof...(_UElements))
> @@ -1042,8 +1490,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             const tuple<_UElements...>& __in)
>       : _Inherited(__tag, __a,
> -                  static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
> -     { }
> +                  static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
> +     { __glibcxx_no_dangling_refs(const _UElements&); }
>  
>        template<typename _Alloc, typename... _UElements,
>              bool _Valid = (sizeof...(_Elements) == sizeof...(_UElements))
> @@ -1053,8 +1501,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             tuple<_UElements...>&& __in)
>       : _Inherited(__tag, __a,
> -                  static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
> -     { }
> +                  static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
>  
>        template<typename _Alloc, typename... _UElements,
>              bool _Valid = (sizeof...(_Elements) == sizeof...(_UElements))
> @@ -1065,37 +1513,180 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             tuple<_UElements...>&& __in)
>       : _Inherited(__tag, __a,
> -                  static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
> -     { }
> -
> -#if __cplusplus > 202002L
> -      template<typename _Alloc, typename... _UElements>
> -     requires (sizeof...(_Elements) == sizeof...(_UElements))
> -       && (!__use_other_ctor<tuple<_UElements...>&>())
> -       && __constructible<_UElements&...>
> -     explicit(!__convertible<_UElements&...>)
> -     constexpr
> -     tuple(allocator_arg_t __tag, const _Alloc& __a,
> -           tuple<_UElements...>& __in)
> -     : _Inherited(__tag, __a,
> -                  static_cast<_Tuple_impl<0, _UElements...>&>(__in))
> -     { }
> -
> -      template<typename _Alloc, typename... _UElements>
> -     requires (sizeof...(_Elements) == sizeof...(_UElements))
> -       && (!__use_other_ctor<const tuple<_UElements...>>())
> -       && __constructible<const _UElements...>
> -     explicit(!__convertible<const _UElements...>)
> -     constexpr
> -     tuple(allocator_arg_t __tag, const _Alloc& __a,
> -           const tuple<_UElements...>&& __in)
> -     : _Inherited(__tag, __a,
> -                  static_cast<const _Tuple_impl<0, _UElements...>&&>(__in))
> -     { }
> -#endif // C++23
> +                  static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
> +     { __glibcxx_no_dangling_refs(_UElements&&); }
> +#endif // concepts && conditional_explicit
>  
>        // tuple assignment
>  
> +#if __cpp_concepts // >= C++20
> +    private:
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __assignable()
> +     {
> +       if constexpr (sizeof...(_UTypes) == sizeof...(_Elements))
> +         return (is_assignable_v<_Elements&, _UTypes> && ...);
> +       else
> +         return false;
> +     }
> +
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __nothrow_assignable()
> +     {
> +       if constexpr (sizeof...(_UTypes) == sizeof...(_Elements))
> +         return (is_nothrow_assignable_v<_Elements&, _UTypes> && ...);
> +       else
> +         return false;
> +     }
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      template<typename... _UTypes>
> +     static consteval bool
> +     __const_assignable()
> +     {
> +       if constexpr (sizeof...(_UTypes) == sizeof...(_Elements))
> +         return (is_assignable_v<const _Elements&, _UTypes> && ...);
> +       else
> +         return false;
> +     }
> +#endif // C++23
> +
> +    public:
> +
> +      tuple& operator=(const tuple& __u) = delete;
> +
> +      constexpr tuple&
> +      operator=(const tuple& __u)
> +      noexcept(__nothrow_assignable<const _Elements&...>())
> +      requires (__assignable<const _Elements&...>())
> +      {
> +     this->_M_assign(__u);
> +     return *this;
> +      }
> +
> +      constexpr tuple&
> +      operator=(tuple&& __u)
> +      noexcept(__nothrow_assignable<_Elements...>())
> +      requires (__assignable<_Elements...>())
> +      {
> +     this->_M_assign(std::move(__u));
> +     return *this;
> +      }
> +
> +      template<typename... _UTypes>
> +     requires (__assignable<const _UTypes&...>())
> +     constexpr tuple&
> +     operator=(const tuple<_UTypes...>& __u)
> +     noexcept(__nothrow_assignable<const _UTypes&...>())
> +     {
> +       this->_M_assign(__u);
> +       return *this;
> +     }
> +
> +      template<typename... _UTypes>
> +     requires (__assignable<_UTypes...>())
> +     constexpr tuple&
> +     operator=(tuple<_UTypes...>&& __u)
> +     noexcept(__nothrow_assignable<_UTypes...>())
> +     {
> +       this->_M_assign(std::move(__u));
> +       return *this;
> +     }
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      constexpr const tuple&
> +      operator=(const tuple& __u) const
> +      requires (__const_assignable<const _Elements&...>())
> +      {
> +     this->_M_assign(__u);
> +     return *this;
> +      }
> +
> +      constexpr const tuple&
> +      operator=(tuple&& __u) const
> +      requires (__const_assignable<_Elements...>())
> +      {
> +     this->_M_assign(std::move(__u));
> +     return *this;
> +      }
> +
> +      template<typename... _UTypes>
> +     constexpr const tuple&
> +     operator=(const tuple<_UTypes...>& __u) const
> +     requires (__const_assignable<const _UTypes&...>())
> +     {
> +       this->_M_assign(__u);
> +       return *this;
> +     }
> +
> +      template<typename... _UTypes>
> +     constexpr const tuple&
> +     operator=(tuple<_UTypes...>&& __u) const
> +     requires (__const_assignable<_UTypes...>())
> +     {
> +       this->_M_assign(std::move(__u));
> +       return *this;
> +     }
> +#endif // C++23
> +
> +      template<typename _U1, typename _U2>
> +     requires (__assignable<const _U1&, const _U2&>())
> +     constexpr tuple&
> +     operator=(const pair<_U1, _U2>& __u)
> +     noexcept(__nothrow_assignable<const _U1&, const _U2&>())
> +     {
> +       this->_M_head(*this) = __u.first;
> +       this->_M_tail(*this)._M_head(*this) = __u.second;
> +       return *this;
> +     }
> +
> +      template<typename _U1, typename _U2>
> +     requires (__assignable<_U1, _U2>())
> +     constexpr tuple&
> +     operator=(pair<_U1, _U2>&& __u)
> +     noexcept(__nothrow_assignable<_U1, _U2>())
> +     {
> +       this->_M_head(*this) = std::forward<_U1>(__u.first);
> +       this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__u.second);
> +       return *this;
> +     }
> +
> +#if __cpp_lib_ranges_zip // >= C++23
> +      template<typename _U1, typename _U2>
> +     requires (__const_assignable<const _U1&, const _U2>())
> +     constexpr const tuple&
> +     operator=(const pair<_U1, _U2>& __u) const
> +     {
> +       this->_M_head(*this) = __u.first;
> +       this->_M_tail(*this)._M_head(*this) = __u.second;
> +       return *this;
> +     }
> +
> +      template<typename _U1, typename _U2>
> +     requires (__const_assignable<_U1, _U2>())
> +     constexpr const tuple&
> +     operator=(pair<_U1, _U2>&& __u) const
> +     {
> +       this->_M_head(*this) = std::forward<_U1>(__u.first);
> +       this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__u.second);
> +       return *this;
> +     }
> +#endif // C++23
> +
> +#if 0 && __cpp_lib_tuple_like // >= C++23
> +      template<__tuple_like _UTuple>
> +     constexpr tuple&
> +     operator=(_UTuple&& __u);
> +
> +      template<__tuple_like _UTuple>
> +     constexpr tuple&
> +     operator=(_UTuple&& __u) const;
> +#endif // C++23
> +
> +#else  // concepts

// ! concepts

> +
>        _GLIBCXX20_CONSTEXPR
>        tuple&
>        operator=(__conditional_t<__assignable<const _Elements&...>(),
> @@ -1137,44 +1728,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         this->_M_assign(std::move(__in));
>         return *this;
>       }
> -
> -#if __cplusplus > 202002L
> -      constexpr const tuple&
> -      operator=(const tuple& __in) const
> -      requires (is_copy_assignable_v<const _Elements> && ...)
> -      {
> -     this->_M_assign(__in);
> -     return *this;
> -      }
> -
> -      constexpr const tuple&
> -      operator=(tuple&& __in) const
> -      requires (is_assignable_v<const _Elements&, _Elements> && ...)
> -      {
> -     this->_M_assign(std::move(__in));
> -     return *this;
> -      }
> -
> -      template<typename... _UElements>
> -     constexpr const tuple&
> -     operator=(const tuple<_UElements...>& __in) const
> -     requires (sizeof...(_Elements) == sizeof...(_UElements))
> -       && (is_assignable_v<const _Elements&, const _UElements&> && ...)
> -     {
> -       this->_M_assign(__in);
> -       return *this;
> -     }
> -
> -      template<typename... _UElements>
> -     constexpr const tuple&
> -     operator=(tuple<_UElements...>&& __in) const
> -     requires (sizeof...(_Elements) == sizeof...(_UElements))
> -       && (is_assignable_v<const _Elements&, _UElements> && ...)
> -     {
> -       this->_M_assign(std::move(__in));
> -       return *this;
> -     }
> -#endif // C++23
> +#endif // concepts
>  
>        // tuple swap
>        _GLIBCXX20_CONSTEXPR
> @@ -1183,7 +1737,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
>        { _Inherited::_M_swap(__in); }
>  
> -#if __cplusplus > 202002L
> +#if __cpp_lib_ranges_zip // >= C++23
>        // As an extension, we constrain the const swap member function in 
> order
>        // to continue accepting explicit instantiation of tuples whose 
> elements
>        // are not all const swappable.  Without this constraint, such an
> @@ -1233,6 +1787,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t, const _Alloc&, const tuple&) noexcept { }
>      };
>  
> +#if !(__cpp_concepts && __cpp_conditional_explicit) // >= C++20
>    /// Partial specialization, 2-element tuple.
>    /// Includes construction and assignment from a pair.
>    template<typename _T1, typename _T2>
> @@ -1300,15 +1855,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       static constexpr bool __is_alloc_arg()
>       { return is_same<__remove_cvref_t<_U1>, allocator_arg_t>::value; }
>  
> -#if __cplusplus > 202002L
> -      template<typename _U1, typename _U2>
> -     static constexpr bool __constructible
> -       = _TCC<true>::template __constructible<_U1, _U2>::value;
> -
> -      template<typename _U1, typename _U2>
> -     static constexpr bool __convertible
> -       = _TCC<true>::template __convertible<_U1, _U2>::value;
> -#endif // C++23
> +      /// @cond undocumented
> +#undef __glibcxx_no_dangling_refs
> +      // Error if construction from _U1 and _U2 would create a dangling ref.
> +#if __has_builtin(__reference_constructs_from_temporary) \
> +      && defined _GLIBCXX_DEBUG
> +# define __glibcxx_no_dangling_refs(_U1, _U2) \
> +  static_assert(!__reference_constructs_from_temporary(_T1, _U1) \
> +            && !__reference_constructs_from_temporary(_T2, _U2), \
> +             "std::tuple constructor creates a dangling reference")
> +#else
> +# define __glibcxx_no_dangling_refs(_U1, _U2)
> +#endif
> +      /// @endcond
>  
>      public:
>        template<bool _Dummy = true,
> @@ -1344,14 +1903,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       constexpr
>       tuple(_U1&& __a1, _U2&& __a2)
>       noexcept(__nothrow_constructible<_U1, _U2>())
> -     : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { }
> +     : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _U1, typename _U2,
>              _ExplicitCtor<!__is_alloc_arg<_U1>(), _U1, _U2> = false>
>       explicit constexpr
>       tuple(_U1&& __a1, _U2&& __a2)
>       noexcept(__nothrow_constructible<_U1, _U2>())
> -     : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { }
> +     : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        constexpr tuple(const tuple&) = default;
>  
> @@ -1362,60 +1923,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       constexpr
>       tuple(const tuple<_U1, _U2>& __in)
>       noexcept(__nothrow_constructible<const _U1&, const _U2&>())
> -     : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in)) { }
> +     : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _U1, typename _U2,
>              _ExplicitCtor<true, const _U1&, const _U2&> = false>
>       explicit constexpr
>       tuple(const tuple<_U1, _U2>& __in)
>       noexcept(__nothrow_constructible<const _U1&, const _U2&>())
> -     : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in)) { }
> +     : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _U1, typename _U2,
>              _ImplicitCtor<true, _U1, _U2> = true>
>       constexpr
>       tuple(tuple<_U1, _U2>&& __in)
>       noexcept(__nothrow_constructible<_U1, _U2>())
> -     : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
> +     : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _U1, typename _U2,
>              _ExplicitCtor<true, _U1, _U2> = false>
>       explicit constexpr
>       tuple(tuple<_U1, _U2>&& __in)
>       noexcept(__nothrow_constructible<_U1, _U2>())
> -     : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
> -
> -#if __cplusplus > 202002L
> -      template<typename _U1, typename _U2>
> -     requires __constructible<_U1&, _U2&>
> -     explicit(!__convertible<_U1&, _U2&>)
> -     constexpr
> -     tuple(tuple<_U1, _U2>& __in)
> -     noexcept(__nothrow_constructible<_U1&, _U2&>())
> -     : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&>(__in)) { }
> -
> -      template<typename _U1, typename _U2>
> -     requires __constructible<const _U1, const _U2>
> -     explicit(!__convertible<const _U1, const _U2>)
> -     constexpr
> -     tuple(const tuple<_U1, _U2>&& __in)
> -     noexcept(__nothrow_constructible<const _U1, const _U2>())
> -     : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in)) { }
> -#endif // C++23
> +     : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _U1, typename _U2,
>              _ImplicitCtor<true, const _U1&, const _U2&> = true>
>       constexpr
>       tuple(const pair<_U1, _U2>& __in)
>       noexcept(__nothrow_constructible<const _U1&, const _U2&>())
> -     : _Inherited(__in.first, __in.second) { }
> +     : _Inherited(__in.first, __in.second)
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _U1, typename _U2,
>              _ExplicitCtor<true, const _U1&, const _U2&> = false>
>       explicit constexpr
>       tuple(const pair<_U1, _U2>& __in)
>       noexcept(__nothrow_constructible<const _U1&, const _U2&>())
> -     : _Inherited(__in.first, __in.second) { }
> +     : _Inherited(__in.first, __in.second)
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _U1, typename _U2,
>              _ImplicitCtor<true, _U1, _U2> = true>
> @@ -1423,7 +1972,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(pair<_U1, _U2>&& __in)
>       noexcept(__nothrow_constructible<_U1, _U2>())
>       : _Inherited(std::forward<_U1>(__in.first),
> -                  std::forward<_U2>(__in.second)) { }
> +                  std::forward<_U2>(__in.second))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _U1, typename _U2,
>              _ExplicitCtor<true, _U1, _U2> = false>
> @@ -1431,26 +1981,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(pair<_U1, _U2>&& __in)
>       noexcept(__nothrow_constructible<_U1, _U2>())
>       : _Inherited(std::forward<_U1>(__in.first),
> -                  std::forward<_U2>(__in.second)) { }
> -
> -#if __cplusplus > 202002L
> -      template<typename _U1, typename _U2>
> -     requires __constructible<_U1&, _U2&>
> -     explicit(!__convertible<_U1&, _U2&>)
> -     constexpr
> -     tuple(pair<_U1, _U2>& __in)
> -     noexcept(__nothrow_constructible<_U1&, _U2&>())
> -     : _Inherited(__in.first, __in.second) { }
> -
> -      template<typename _U1, typename _U2>
> -     requires __constructible<const _U1, const _U2>
> -     explicit(!__convertible<const _U1, const _U2>)
> -     constexpr
> -     tuple(const pair<_U1, _U2>&& __in)
> -     noexcept(__nothrow_constructible<const _U1, const _U2>())
> -     : _Inherited(std::forward<const _U1>(__in.first),
> -                  std::forward<const _U2>(__in.second)) { }
> -#endif // C++23
> +                  std::forward<_U2>(__in.second))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        // Allocator-extended constructors.
>  
> @@ -1480,7 +2012,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a, _U1&& __a1, _U2&& __a2)
>       : _Inherited(__tag, __a, std::forward<_U1>(__a1),
> -                  std::forward<_U2>(__a2)) { }
> +                  std::forward<_U2>(__a2))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ExplicitCtor<true, _U1, _U2> = false>
> @@ -1489,7 +2022,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             _U1&& __a1, _U2&& __a2)
>       : _Inherited(__tag, __a, std::forward<_U1>(__a1),
> -                  std::forward<_U2>(__a2)) { }
> +                  std::forward<_U2>(__a2))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _Alloc>
>       _GLIBCXX20_CONSTEXPR
> @@ -1507,8 +2041,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             const tuple<_U1, _U2>& __in)
>       : _Inherited(__tag, __a,
> -                  static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
> -     { }
> +                  static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ExplicitCtor<true, const _U1&, const _U2&> = false>
> @@ -1517,15 +2051,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             const tuple<_U1, _U2>& __in)
>       : _Inherited(__tag, __a,
> -                  static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
> -     { }
> +                  static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ImplicitCtor<true, _U1, _U2> = true>
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_U1, _U2>&& __in)
>       : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
> -     { }
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ExplicitCtor<true, _U1, _U2> = false>
> @@ -1533,36 +2067,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_U1, _U2>&& __in)
>       : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
> -     { }
> -
> -#if __cplusplus > 202002L
> -      template<typename _Alloc, typename _U1, typename _U2>
> -     requires __constructible<_U1&, _U2&>
> -     explicit(!__convertible<_U1&, _U2&>)
> -     constexpr
> -     tuple(allocator_arg_t __tag, const _Alloc& __a,
> -           tuple<_U1, _U2>& __in)
> -     : _Inherited(__tag, __a,
> -                  static_cast<_Tuple_impl<0, _U1, _U2>&>(__in))
> -     { }
> -
> -      template<typename _Alloc, typename _U1, typename _U2>
> -     requires __constructible<const _U1, const _U2>
> -     explicit(!__convertible<const _U1, const _U2>)
> -     constexpr
> -     tuple(allocator_arg_t __tag, const _Alloc& __a,
> -           const tuple<_U1, _U2>&& __in)
> -     : _Inherited(__tag, __a,
> -                  static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in))
> -     { }
> -#endif // C++23
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ImplicitCtor<true, const _U1&, const _U2&> = true>
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             const pair<_U1, _U2>& __in)
> -     : _Inherited(__tag, __a, __in.first, __in.second) { }
> +     : _Inherited(__tag, __a, __in.first, __in.second)
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ExplicitCtor<true, const _U1&, const _U2&> = false>
> @@ -1570,14 +2083,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a,
>             const pair<_U1, _U2>& __in)
> -     : _Inherited(__tag, __a, __in.first, __in.second) { }
> +     : _Inherited(__tag, __a, __in.first, __in.second)
> +     { __glibcxx_no_dangling_refs(const _U1&, const _U2&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ImplicitCtor<true, _U1, _U2> = true>
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in)
>       : _Inherited(__tag, __a, std::forward<_U1>(__in.first),
> -                  std::forward<_U2>(__in.second)) { }
> +                  std::forward<_U2>(__in.second))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        template<typename _Alloc, typename _U1, typename _U2,
>              _ExplicitCtor<true, _U1, _U2> = false>
> @@ -1585,25 +2100,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _GLIBCXX20_CONSTEXPR
>       tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in)
>       : _Inherited(__tag, __a, std::forward<_U1>(__in.first),
> -                  std::forward<_U2>(__in.second)) { }
> -
> -#if __cplusplus > 202002L
> -      template<typename _Alloc, typename _U1, typename _U2>
> -     requires __constructible<_U1&, _U2&>
> -     explicit(!__convertible<_U1&, _U2&>)
> -     constexpr
> -     tuple(allocator_arg_t __tag, const _Alloc& __a,
> -           pair<_U1, _U2>& __in)
> -     : _Inherited(__tag, __a, __in.first, __in.second) { }
> -
> -      template<typename _Alloc, typename _U1, typename _U2>
> -     requires __constructible<const _U1, const _U2>
> -     explicit(!__convertible<const _U1, const _U2>)
> -     constexpr
> -     tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>&& 
> __in)
> -     : _Inherited(__tag, __a, std::forward<const _U1>(__in.first),
> -                  std::forward<const _U2>(__in.second)) { }
> -#endif // C++23
> +                  std::forward<_U2>(__in.second))
> +     { __glibcxx_no_dangling_refs(_U1&&, _U2&&); }
>  
>        // Tuple assignment.
>  
> @@ -1649,44 +2147,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         return *this;
>       }
>  
> -#if __cplusplus > 202002L
> -      constexpr const tuple&
> -      operator=(const tuple& __in) const
> -      requires is_copy_assignable_v<const _T1> && is_copy_assignable_v<const 
> _T2>
> -      {
> -     this->_M_assign(__in);
> -     return *this;
> -      }
> -
> -      constexpr const tuple&
> -      operator=(tuple&& __in) const
> -      requires is_assignable_v<const _T1&, _T1> && is_assignable_v<const 
> _T2, _T2>
> -      {
> -     this->_M_assign(std::move(__in));
> -     return *this;
> -      }
> -
> -      template<typename _U1, typename _U2>
> -     constexpr const tuple&
> -     operator=(const tuple<_U1, _U2>& __in) const
> -     requires is_assignable_v<const _T1&, const _U1&>
> -       && is_assignable_v<const _T2&, const _U2&>
> -     {
> -       this->_M_assign(__in);
> -       return *this;
> -     }
> -
> -      template<typename _U1, typename _U2>
> -     constexpr const tuple&
> -     operator=(tuple<_U1, _U2>&& __in) const
> -     requires is_assignable_v<const _T1&, _U1>
> -       && is_assignable_v<const _T2&, _U2>
> -     {
> -       this->_M_assign(std::move(__in));
> -       return *this;
> -     }
> -#endif // C++23
> -
>        template<typename _U1, typename _U2>
>       _GLIBCXX20_CONSTEXPR
>       __enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
> @@ -1709,47 +2169,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         return *this;
>       }
>  
> -#if __cplusplus > 202002L
> -      template<typename _U1, typename _U2>
> -     constexpr const tuple&
> -     operator=(const pair<_U1, _U2>& __in) const
> -     requires is_assignable_v<const _T1&, const _U1&>
> -       && is_assignable_v<const _T2&, const _U2&>
> -     {
> -       this->_M_head(*this) = __in.first;
> -       this->_M_tail(*this)._M_head(*this) = __in.second;
> -       return *this;
> -     }
> -
> -      template<typename _U1, typename _U2>
> -     constexpr const tuple&
> -     operator=(pair<_U1, _U2>&& __in) const
> -     requires is_assignable_v<const _T1&, _U1>
> -       && is_assignable_v<const _T2&, _U2>
> -     {
> -       this->_M_head(*this) = std::forward<_U1>(__in.first);
> -       this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
> -       return *this;
> -     }
> -#endif // C++23
> -
>        _GLIBCXX20_CONSTEXPR
>        void
>        swap(tuple& __in)
>        noexcept(__and_<__is_nothrow_swappable<_T1>,
>                     __is_nothrow_swappable<_T2>>::value)
>        { _Inherited::_M_swap(__in); }
> -
> -#if __cplusplus > 202002L
> -      constexpr void
> -      swap(const tuple& __in) const
> -      noexcept(__and_v<__is_nothrow_swappable<const _T1>,
> -                    __is_nothrow_swappable<const _T2>>)
> -      requires is_swappable_v<const _T1> && is_swappable_v<const _T2>
> -      { _Inherited::_M_swap(__in); }
> -#endif // C++23
>      };
> -
> +#endif // concepts && conditional_explicit
>  
>    /// class tuple_size
>    template<typename... _Elements>
> @@ -2174,7 +2601,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      noexcept(noexcept(__x.swap(__y)))
>      { __x.swap(__y); }
>  
> -#if __cplusplus > 202002L
> +#if __cpp_lib_ranges_zip // >= C++23
>    template<typename... _Elements>
>      requires (is_swappable_v<const _Elements> && ...)
>      constexpr void
> @@ -2329,7 +2756,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      }
>  #endif
>  
> -#if __cplusplus > 202002L
> +#if __cpp_lib_ranges_zip // >= C++23
>    template<typename... _TTypes, typename... _UTypes,
>          template<typename> class _TQual, template<typename> class _UQual>
>      requires requires { typename tuple<common_reference_t<_TQual<_TTypes>, 
> _UQual<_UTypes>>...>; }
> @@ -2344,6 +2771,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  
>    /// @}
>  
> +#undef __glibcxx_no_dangling_refs
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace std
>  
> diff --git a/libstdc++-v3/include/std/type_traits 
> b/libstdc++-v3/include/std/type_traits
> index b6b680a3c58..a9bb2806ca9 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -1306,6 +1306,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       "template argument must be a complete class or an unbounded array");
>      };
>  
> +#if __cpp_variable_templates && __cpp_concepts
> +  template<typename _Tp>
> +    constexpr bool __is_implicitly_default_constructible_v
> +      = requires (void(&__f)(_Tp)) { __f({}); };
> +
> +  template<typename _Tp>
> +    struct __is_implicitly_default_constructible
> +    : __bool_constant<__is_implicitly_default_constructible_v<_Tp>>
> +    { };
> +#else
>    struct __do_is_implicitly_default_constructible_impl
>    {
>      template <typename _Tp>
> @@ -1335,6 +1345,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      : public __and_<__is_constructible_impl<_Tp>,
>                   __is_implicitly_default_constructible_safe<_Tp>>::type
>      { };
> +#endif
>  
>    /// is_trivially_copy_constructible
>    template<typename _Tp>
> diff --git a/libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc 
> b/libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc
> new file mode 100644
> index 00000000000..c6c8e0c3ef4
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc
> @@ -0,0 +1,105 @@
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-Wno-unused-variable" }
> +// { dg-additional-options "-D_GLIBCXX_DEBUG" { target c++17_down } }
> +
> +#include <tuple>
> +#include <utility>
> +
> +#if __cplusplus >= 202002L
> +// For C++20 and later, constructors are constrained to disallow dangling.
> +static_assert(!std::is_constructible_v<std::tuple<const int&, int>, long, 
> int>);
> +static_assert(!std::is_constructible_v<std::tuple<int, const int&>, int, 
> long>);
> +static_assert(!std::is_constructible_v<std::tuple<const int&, int>,
> +                                    std::tuple<long, long>>);
> +static_assert(!std::is_constructible_v<std::tuple<int, const int&>,
> +                                    std::tuple<long, long>>);
> +static_assert(!std::is_constructible_v<std::tuple<const int&, int>,
> +                                    const std::tuple<long, long>&>);
> +static_assert(!std::is_constructible_v<std::tuple<int, const int&>,
> +                                    const std::tuple<long, long>&>);
> +static_assert(!std::is_constructible_v<std::tuple<const int&, int>,
> +                                    std::pair<long, long>>);
> +static_assert(!std::is_constructible_v<std::tuple<int, const int&>,
> +                                    std::pair<long, long>>);
> +static_assert(!std::is_constructible_v<std::tuple<const int&, int>,
> +                                    const std::pair<long, long>&>);
> +static_assert(!std::is_constructible_v<std::tuple<int, const int&>,
> +                                    const std::pair<long, long>&>);
> +#endif
> +
> +void
> +test_ary_ctors()
> +{
> +  std::tuple<const int&, int> t1(1L, 2);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 33 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 33 }
> +
> +  std::tuple<int, const int&> t2(1, 2L);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 37 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 37 }
> +
> +  std::tuple<const int&, const int&> t3(1L, 2L);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 41 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 41 }
> +
> +  std::tuple<const int&, const int&> t4(std::pair<long, int>{});
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 45 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 45 }
> +
> +  std::pair<int, long> p;
> +  std::tuple<const int&, const int&> t5(p);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 50 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 50 }
> +}
> +
> +void
> +test_converting_ctors()
> +{
> +  std::tuple<long, long> t0;
> +
> +  std::tuple<const int&, int> t1(t0);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 60 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 60 }
> +
> +  std::tuple<int, const int&> t2(t0);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 64 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 64 }
> +
> +  std::tuple<const int&, const int&> t3(t0);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 68 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 68 }
> +
> +  std::tuple<const int&, int> t4(std::move(t0));
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 72 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 72 }
> +
> +  std::tuple<int, const int&> t5(std::move(t0));
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 76 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 76 }
> +
> +  std::tuple<const int&, const int&> t6(std::move(t0));
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 80 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 80 }
> +
> +  std::pair<long, long> p0;
> +  std::tuple<const int&, int> t7(p0);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 85 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 85 }
> +
> +  std::tuple<int, const int&> t8(p0);
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 89 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 89 }
> +
> +  std::tuple<const int&, int> t9(std::move(p0));
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 93 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 93 }
> +
> +  std::tuple<int, const int&> t10(std::move(p0));
> +  // { dg-error "here" "" { target { c++17_down && hosted } } 97 }
> +  // { dg-error "use of deleted function" "" { target c++20 } 97 }
> +}
> +
> +// TODO: test allocator-extended ctors
> +// TODO test 1-tuple or 3-tuple, not just 2-tuple
> +
> +// { dg-error "static assert.* dangling reference" "" { target { c++17_down 
> && hosted } } 0 }
> -- 
> 2.43.0
> 
> 

Reply via email to