I have tested following on your patches:
#if __cpp_lib_tuple_like // >= C++23
#include <tuple>

using std::tuple;

static_assert( std::is_same_v<
  common_reference_t<tuple<reference_wrapper<int>>, tuple<int>>,
  tuple<int>>
);
static_assert( std::is_same_v<
  common_reference_t<const tuple<reference_wrapper<int>&>, tuple<int&>>,
  tuple<int&>>
);
static_assert( std::is_same_v<
  common_reference_t<tuple<reference_wrapper<int>>, tuple<int>&>,
  tuple<int&>>
);
static_assert( std::is_same_v<
  common_reference_t<tuple<reference_wrapper<const int>>, tuple<int&>>,
  tuple<const int&>>
);
static_assert( std::is_same_v<
  common_reference_t<tuple<reference_wrapper<int>>, tuple<const int&>>,
  tuple<const int&>>
);
static_assert( std::is_same_v<
  common_reference_t<tuple<reference_wrapper<int>>, const tuple<int>&>,
  tuple<const int&>>
);
#endif

On Mon, Oct 13, 2025 at 9:25 AM Tomasz Kaminski <[email protected]> wrote:

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