[PATCH 2/2] libstdc++: Implement P2655R3 common_reference of reference_wrapper
Tomasz Kaminski
tkaminsk@redhat.com
Mon Oct 13 07:25:10 GMT 2025
On Mon, Oct 13, 2025 at 6:29 AM Patrick Palka <ppalka@redhat.com> wrote:
> On Wed, 8 Oct 2025, Tomasz Kaminski wrote:
>
> >
> >
> > On Tue, Oct 7, 2025 at 5:10 AM Patrick Palka <ppalka@redhat.com> 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
> >
> >
> >
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251013/73bcc8a0/attachment-0001.htm>
More information about the Libstdc++
mailing list