On Mon, Oct 13, 2025 at 6:29 AM Patrick Palka <[email protected]> wrote:

> On Wed, 8 Oct 2025, Tomasz Kaminski wrote:
>
> >
> >
> > 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.
>
> But in 'requires { Y; } && convertible_to<X, Y>' we first instantiate
> Y's mapped template argument, whereas in just 'convertible_to<X, Y>' we
> first instantiate X's mapped template argument.  Such a change in the
> order of instantiation could be observable; I'm not positive it's
> unobservable in this particular scenario.  So I'd prefer following the
> standard.  If it's truly unnecessary maybe we should remove it
> editiorially from the standard first.
>
> >       +       && 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.
>
> Sounds good
>
> >
> >            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.
>
> I added some extra testcases, but I'm afraid I don't know precisely what
> you want added.  I couldn't come up with a valid pair/tuple +
> reference_wrapper testcase for which common_reference_t is well-formed,
> or a ref<ref<T>> testcase for which behavior is changed by this patch.
> If you have provide concrete testcases I can add them.
>
Becaues of the basic_common_reference for tuple + tuple-like specialization
(https://eel.is/c++draft/tuple.common.ref).
I would expect that  common_reference_t<tuple<reference_wraper<T>> cv1
ref1, tuple<U> cv2 ref2>>
produces tuple<common_reference_<T&, U cv2 ref2>>
But, I think we could add them as separate commits later.

Examples would be:
tuple<refernce_wrapper<int>> + tuple<int> -> tuple<int>
tuple<refernce_wrapper<int>> + tuple<int>& -> tuple<int&>
tuple<refernce_wrapper<int>> + tuple<int&> -> tuple<int&>

tuple<reference_wrapper<int>> + const tuple<int> -> tuple<int>
tuple<refernce_wrapper<int>> + const tuple<int>& -> tuple<const int&>

// reference from reference wrapper is ignored
tuple<const refernce_wrapper<int>&> + tuple<int>& -> tuple<int&>
const tuple<refernce_wrapper<int>>& + tuple<int>& -> tuple<int&>

tuple<reference_wrapper<int>> + tuple<reference_wrapper<const int>> ->
tuple<reference_wrapper<const int>>?


> >       +
> >       +#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