On Wed, Sep 3, 2025 at 11:56 AM Jonathan Wakely <[email protected]> wrote:

> Make the std::get<T> overloads for rvalues use std::forward<T>(p.first)
> not std::move(p.first), so that lvalue reference members are not
> incorrectly converted to rvalues.
>
> It might appear that std::move(p).first would also work, but the
> language rules say that for std::pair<T&&, U> that would produce T&
> rather than the expected T&&. Additional tests are added to verify all
> combinations of reference members, value categories, and
> const-qualification.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/121745
>         * include/bits/stl_pair.h (get): Use forward instead of move in
>         std::get<T> overloads for rvalue pairs.
>         * testsuite/20_util/pair/astuple/get_by_type.cc: Check all value
>         categories and cv-qualification.
> ---
>
LGTM. Thanks.

>  libstdc++-v3/include/bits/stl_pair.h          |  8 +--
>  .../20_util/pair/astuple/get_by_type.cc       | 52 +++++++++++++++++++
>  2 files changed, 56 insertions(+), 4 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_pair.h
> b/libstdc++-v3/include/bits/stl_pair.h
> index 2de7439ac6a3..231d0bbd1f49 100644
> --- a/libstdc++-v3/include/bits/stl_pair.h
> +++ b/libstdc++-v3/include/bits/stl_pair.h
> @@ -1315,12 +1315,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    template <typename _Tp, typename _Up>
>      constexpr _Tp&&
>      get(pair<_Tp, _Up>&& __p) noexcept
> -    { return std::move(__p.first); }
> +    { return std::forward<_Tp>(__p.first); }
>
>    template <typename _Tp, typename _Up>
>      constexpr const _Tp&&
>      get(const pair<_Tp, _Up>&& __p) noexcept
> -    { return std::move(__p.first); }
> +    { return std::forward<const _Tp>(__p.first); }
>
>    template <typename _Tp, typename _Up>
>      constexpr _Tp&
> @@ -1335,12 +1335,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    template <typename _Tp, typename _Up>
>      constexpr _Tp&&
>      get(pair<_Up, _Tp>&& __p) noexcept
> -    { return std::move(__p.second); }
> +    { return std::forward<_Tp>(__p.second); }
>
>    template <typename _Tp, typename _Up>
>      constexpr const _Tp&&
>      get(const pair<_Up, _Tp>&& __p) noexcept
> -    { return std::move(__p.second); }
> +    { return std::forward<const _Tp>(__p.second); }
>  #endif // __glibcxx_tuples_by_type
>
>
> diff --git a/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc
> b/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc
> index 33ebf7a46b90..05a61c3f302c 100644
> --- a/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc
> +++ b/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc
> @@ -33,3 +33,55 @@ void test01()
>    const int&&  cpsecond __attribute__((unused)) =
>      std::get<int>(std::move(cp));
>  }
> +
> +// PR libstdc++/121745 return of get(pair<_Up, _Tp>&& __p) may be
> ill-formed
> +void
> +test_pr121745(std::pair<float&, int&> p)
> +{
> +  float& pfirst = std::get<float&>(std::move(p));
> +  int& psecond  = std::get<int&>(std::move(p));
> +
> +  const auto& p2 = p;
> +  float& p2first = std::get<float&>(std::move(p2));
> +  int& p2second  = std::get<int&>(std::move(p2));
> +}
> +
> +template<typename T, typename Pair>
> +using get_t = decltype(std::get<T>(std::declval<Pair>()));
> +
> +// Check that get<T>(Pair) returns Ret
> +template<typename T, typename Pair, typename Ret>
> +constexpr bool verify = std::is_same<get_t<T, Pair>, Ret>::value;
> +
> +template<typename T1, typename T2>
> +void
> +check()
> +{
> +  // Overloads for accessing first member
> +  static_assert( verify<T1, std::pair<T1, T2>&, T1&>,
> +                "T1& get(pair<T1, T2>&)" );
> +  static_assert( verify<T1, const std::pair<T1, T2>&, const T1&>,
> +                "const T1& get(const pair<T1, T2>&)" );
> +  static_assert( verify<T1, std::pair<T1, T2>&&, T1&&>,
> +                "T1&& get(pair<T1, T2>&&)" );
> +  static_assert( verify<T1, const std::pair<T1, T2>&&, const T1&&>,
> +                "const T1&& get(const pair<T1, T2>&&)" );
> +
> +  // Overloads for accessing second member
> +  static_assert( verify<T2, std::pair<T1, T2>&, T2&>,
> +                "T2& get(pair<T1, T2>&)" );
> +  static_assert( verify<T2, const std::pair<T1, T2>&, const T2&>,
> +                "const T2& get(const pair<T1, T2>&)" );
> +  static_assert( verify<T2, std::pair<T1, T2>&&, T2&&>,
> +                "T2&& get(pair<T1, T2>&&)" );
> +  static_assert( verify<T2, const std::pair<T1, T2>&&, const T2&&>,
> +                "const T2&& get(const pair<T1, T2>&&)" );
> +}
> +
> +void
> +test_all()
> +{
> +  check<float, int>();
> +  check<float&, int&>();
> +  check<float&&, int&&>();
> +}
> --
> 2.51.0
>
>

Reply via email to