libstdc++: Implement rest of P2655R3 common_reference of reference_wrapper

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_wrapper): New.
	* include/bits/version.h: Regenerate.
	* include/std/functional (__glibcxx_want_common_reference_wrapper):
	Define.
	* testsuite/20_util/reference_wrapper/p2655r3.cc: New test.

Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
This commit is contained in:
Patrick Palka 2025-12-05 13:43:40 -05:00
parent a9fd651fbb
commit d1ac432c5a
5 changed files with 128 additions and 0 deletions

View File

@ -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>; }
&& 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

View File

@ -1829,6 +1829,14 @@ ftms = {
};
};
ftms = {
name = common_reference_wrapper;
values = {
v = 202302;
cxxmin = 20; // We treat P2655R3 as a DR against C++20.
};
};
ftms = {
name = formatters;
values = {

View File

@ -2040,6 +2040,16 @@
#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

View File

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

View File

@ -0,0 +1,75 @@
// 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 <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>&> );
static_assert( is_same_v<common_reference_t<reference_wrapper<char>,
reference_wrapper<int>>,
int> );
static_assert( is_same_v<common_reference_t<reference_wrapper<reference_wrapper<int>>,
reference_wrapper<int>>,
reference_wrapper<int>> );
static_assert( is_same_v<common_reference_t<reference_wrapper<int>,
reference_wrapper<reference_wrapper<int>>>,
reference_wrapper<int>> );
struct A { };
struct B { operator A&() const; };
template<typename T, typename U>
concept has_common_reference = requires {
typename std::common_reference_t<T, U>;
};
static_assert( is_same_v<common_reference_t<reference_wrapper<A>, const B&>, A&> );
// reference_wrapper<const B> is not convertible to A&, as it would require two user
// defined conversions.
static_assert( !has_common_reference<A, reference_wrapper<const B>> );
static_assert( !has_common_reference<reference_wrapper<A>, reference_wrapper<const B>> );
struct D1 : A {};
struct D2 : A {};
template<template<class> typename Qual1, template<class> typename Qual2>
struct std::basic_common_reference<D1, D2, Qual1, Qual2>
: std::common_reference<Qual1<A>, Qual2<A>>
{ };
template<template<class> typename Qual1, template<class> typename Qual2>
struct std::basic_common_reference<D2, D1, Qual1, Qual2>
: std::common_reference<Qual1<A>, Qual2<A>>
{ };
static_assert( is_same_v<common_reference_t<D1&, D2&>, A&> );
static_assert( is_same_v<common_reference_t<reference_wrapper<D1>, D2&>, A&> );
static_assert( is_same_v<common_reference_t<D1&, reference_wrapper<D2>>, A&> );
static_assert( !has_common_reference<reference_wrapper<D1>, reference_wrapper<D2>> );