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