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.

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