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