On Tue, Oct 7, 2025 at 5:10 AM Patrick Palka <[email protected]> wrote:

> Tested on x86_64-pc-linux-gnu, does this look OK for trunk? I wonder
> about backports, for either the whole paper or just the common_reference
> change in <type_traits> for avoiding dangling references.
>
> -- >8 --
>
> This patch implements this paper as a DR against C++20 (as do MSVC and
> libc++).
>
>         PR libstdc++/120446
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/refwrap.h (__detail::__is_ref_wrapper):
>         Define as per P2655R3 for C++20.
>         (__detail::__ref_wrap_common_reference_exists_with):
>         Likewise.
>         (basic_common_reference): Define partial specializations using
>         the above as per P2655R3 for C++20.
>         * include/bits/version.def (common_reference): New.
>         (common_reference_wrapper): New.
>         * include/bits/version.h: Regenerate.
>         * include/std/functional (__glibcxx_want_common_reference_wrapper):
>         Define.
>         * include/std/type_traits (__glibcxx_want_common_reference):
>         Define.
>         (__common_reference_impl<T1, T2, 1>): Add pointer convertibility
>         constraints as per P2655R3.
>         * libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc: New
>         test.
>         * libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc: New
>         test.
> ---
>
The comment are mostly for additional tests, and for further separating
changes
to basic_common_reference generic specialization and other.


>  libstdc++-v3/include/bits/refwrap.h           | 34 +++++++++++++++++++
>  libstdc++-v3/include/bits/version.def         | 16 +++++++++
>  libstdc++-v3/include/bits/version.h           | 20 +++++++++++
>  libstdc++-v3/include/std/functional           |  1 +
>  libstdc++-v3/include/std/type_traits          |  7 ++++
>  .../20_util/common_reference/p2655r3.cc       | 15 ++++++++
>  .../20_util/reference_wrapper/p2655r3.cc      | 33 ++++++++++++++++++
>  7 files changed, 126 insertions(+)
>  create mode 100644
> libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc
>  create mode 100644
> libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc
>
> diff --git a/libstdc++-v3/include/bits/refwrap.h
> b/libstdc++-v3/include/bits/refwrap.h
> index 612715e9b7bd..5d9f8c8f49f9 100644
> --- a/libstdc++-v3/include/bits/refwrap.h
> +++ b/libstdc++-v3/include/bits/refwrap.h
> @@ -457,6 +457,40 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type,
> true_type)
>
>    /// @}
>
> +#if __glibcxx_common_reference_wrapper // C++ >= 20
> +  namespace __detail
> +  {
> +    template<typename _Tp>
> +      constexpr bool __is_ref_wrapper = false;
> +
> +    template<typename _Tp>
> +      constexpr bool __is_ref_wrapper<reference_wrapper<_Tp>> = true;
> +
> +    template<typename _Rp, typename _Tp, typename _RQual, typename _TQual>
> +      concept __ref_wrap_common_reference_exists_with =
> __is_ref_wrapper<_Rp>
> +       && requires { typename common_reference_t<typename _Rp::type&,
> _TQual>; }
>
This requires does not seem to be necessary, as common_reference_t<typename
_Rp::type&, _TQual>
is used as convertible_to argument, and if expressions inside
constraints list is ill-formed
the it will evaluate to false. I know that is exactly what standard
specifies, but still.

> +       && convertible_to<_RQual, common_reference_t<typename _Rp::type&,
> _TQual>>;
> +  } // namespace __detail
> +
> +  template<typename _Rp, typename _Tp,
> +          template<typename> class _RQual, template<typename> class
> _TQual>
> +    requires __detail::__ref_wrap_common_reference_exists_with<_Rp, _Tp,
> +
> _RQual<_Rp>, _TQual<_Tp>>
> +      && (!__detail::__ref_wrap_common_reference_exists_with<_Tp, _Rp,
> +                                                            _TQual<_Tp>,
> _RQual<_Rp>>)
> +  struct basic_common_reference<_Rp, _Tp, _RQual, _TQual>
> +  { using type = common_reference_t<typename _Rp::type&, _TQual<_Tp>>; };
> +
> +  template<typename _Tp, typename _Rp,
> +          template<typename> class _TQual, template<typename> class
> _RQual>
> +    requires __detail::__ref_wrap_common_reference_exists_with<_Rp, _Tp,
> +
> _RQual<_Rp>, _TQual<_Tp>>
> +      && (!__detail::__ref_wrap_common_reference_exists_with<_Tp, _Rp,
> +                                                            _TQual<_Tp>,
> _RQual<_Rp>>)
> +  struct basic_common_reference<_Tp, _Rp, _TQual, _RQual>
> +  { using type = common_reference_t<typename _Rp::type&, _TQual<_Tp>>; };
> +#endif
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace std
>
> diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> index 3a26234f87ed..49d8d17e6677 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1774,6 +1774,22 @@ ftms = {
>    };
>  };
>
> +ftms = {
> +  name = common_reference;
> +  values = {
> +    v = 202302;
> +    cxxmin = 20; // We treat P2655R3 as a DR against C++20.
> +  };
> +};
> +
> +ftms = {
> +  name = common_reference_wrapper;
> +  values = {
> +    v = 202302;
> +    cxxmin = 20; // We treat P2655R3 as a DR against C++20.
> +  };
> +};
> +
>  ftms = {
>    name = formatters;
>    values = {
> diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> index 46e4c1121e7a..3176a80f32e3 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -1983,6 +1983,26 @@
>  #endif /* !defined(__cpp_lib_flat_set) &&
> defined(__glibcxx_want_flat_set) */
>  #undef __glibcxx_want_flat_set
>
> +#if !defined(__cpp_lib_common_reference)
> +# if (__cplusplus >= 202002L)
> +#  define __glibcxx_common_reference 202302L
> +#  if defined(__glibcxx_want_all) ||
> defined(__glibcxx_want_common_reference)
> +#   define __cpp_lib_common_reference 202302L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_common_reference) &&
> defined(__glibcxx_want_common_reference) */
> +#undef __glibcxx_want_common_reference
> +
> +#if !defined(__cpp_lib_common_reference_wrapper)
> +# if (__cplusplus >= 202002L)
> +#  define __glibcxx_common_reference_wrapper 202302L
> +#  if defined(__glibcxx_want_all) ||
> defined(__glibcxx_want_common_reference_wrapper)
> +#   define __cpp_lib_common_reference_wrapper 202302L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_common_reference_wrapper) &&
> defined(__glibcxx_want_common_reference_wrapper) */
> +#undef __glibcxx_want_common_reference_wrapper
> +
>  #if !defined(__cpp_lib_formatters)
>  # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
>  #  define __glibcxx_formatters 202302L
> diff --git a/libstdc++-v3/include/std/functional
> b/libstdc++-v3/include/std/functional
> index 8ad73b343bda..6f7f2f627a2a 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -64,6 +64,7 @@
>  #define __glibcxx_want_not_fn
>  #define __glibcxx_want_ranges
>  #define __glibcxx_want_reference_wrapper
> +#define __glibcxx_want_common_reference_wrapper
>  #define __glibcxx_want_transparent_operators
>  #include <bits/version.h>
>
> diff --git a/libstdc++-v3/include/std/type_traits
> b/libstdc++-v3/include/std/type_traits
> index 8b5110464e50..47cbf21b4c1d 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -41,6 +41,7 @@
>
>  #define __glibcxx_want_bool_constant
>  #define __glibcxx_want_bounded_array_traits
> +#define __glibcxx_want_common_reference
>  #define __glibcxx_want_constant_wrapper
>  #define __glibcxx_want_has_unique_object_representations
>  #define __glibcxx_want_integral_constant_callable
> @@ -4223,6 +4224,12 @@ template<typename _Ret, typename _Fn, typename...
> _Args>
>    template<typename _Tp1, typename _Tp2>
>      requires is_reference_v<_Tp1> && is_reference_v<_Tp2>
>        && requires { typename __common_ref<_Tp1, _Tp2>; }
> +#if __cpp_lib_common_reference // C++ >= 20
> +      && is_convertible_v<add_pointer_t<_Tp1>,
> +                         add_pointer_t<__common_ref<_Tp1, _Tp2>>>
> +      && is_convertible_v<add_pointer_t<_Tp2>,
> +                         add_pointer_t<__common_ref<_Tp1, _Tp2>>>
> +#endif
>
I would prefer this change to be in a separate commit from common_reference
one, in case if we get regression from it.


>      struct __common_reference_impl<_Tp1, _Tp2, 1>
>      { using type = __common_ref<_Tp1, _Tp2>; };
>
> diff --git a/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc
> b/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc
> new file mode 100644
> index 000000000000..bc0372f79686
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc
> @@ -0,0 +1,15 @@
> +// P2655R3 - common_reference_t of reference_wrapper Should Be a
> Reference Type
> +// Implemented as a DR against C++20
> +// { dg-do compile { target c++20 } }
> +
> +#include <type_traits>
> +
> +#if __cpp_lib_common_reference != 202302L
> +# error "Feature-test macro __cpp_lib_common_reference has wrong value in
> <type_traits>"
> +#endif
> +
> +struct A { };
> +struct B { operator A&() const; };
> +
> +static_assert(std::is_same_v<std::common_reference_t<A&, const B&>, A&>);
> +static_assert(std::is_same_v<std::common_reference_t<const B&, A&>, A&>);
> diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc
> b/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc
> new file mode 100644
> index 000000000000..f66c2821554a
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc
> @@ -0,0 +1,33 @@
> +// P2655R3 - common_reference_t of reference_wrapper Should Be a
> Reference Type
> +// Implemented as a DR against C++20
> +// { dg-do compile { target c++20 } }
>
Could you add following tests:
* common_reference of reference_wrapper<T> and reference_wrapper<U>,
  including reference_wrapper<reference_wrapper<T>> and reference_wrapper<U>
You could do that by having a wrapper template parameter, and passing
template<typename T> using Id = T; for normal cases
 (I suspect that something may be inconsistent here).

* common_reference of pair/tuple reference wrapper<T> and U&,
  maybe in separate file.

> +
> +#include <functional>
> +
> +#if __cpp_lib_common_reference_wrapper != 202302L
> +# error "Feature-test macro __cpp_lib_common_reference_wrapper has wrong
> value in <functional>"
> +#endif
> +
> +using std::is_same_v;
> +using std::common_reference_t;
> +using std::reference_wrapper;
> +
> +static_assert(is_same_v<common_reference_t<const reference_wrapper<int>&,
> int&>, int&>);
> +static_assert(is_same_v<common_reference_t<int&, const
> reference_wrapper<int>&>, int&>);
> +
> +static_assert(is_same_v<common_reference_t<reference_wrapper<int>, int&>,
> +                       common_reference_t<int&, int&>>);
> +static_assert(is_same_v<common_reference_t<reference_wrapper<int>, const
> int&>,
> +                       common_reference_t<int&, const int&>>);
> +static_assert(is_same_v<common_reference_t<reference_wrapper<const int>,
> int&>,
> +                       common_reference_t<const int&, int&>>);
> +
> +static_assert(is_same_v<common_reference_t<int&, reference_wrapper<int>>,
> +                       common_reference_t<int&, int&>>);
> +static_assert(is_same_v<common_reference_t<const int&,
> reference_wrapper<int>>,
> +                       common_reference_t<int&, const int&>>);
> +static_assert(is_same_v<common_reference_t<int&, reference_wrapper<const
> int>>,
> +                       common_reference_t<const int&, int&>>);
> +
> +static_assert(is_same_v<common_reference_t<reference_wrapper<int>&,
> reference_wrapper<int>&>,
> +                       reference_wrapper<int>&>);
> --
> 2.51.0.433.g45547b60ac
>
>

Reply via email to