diff mbox series

[2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26

Message ID 20240410085039.267589-2-jwakely@redhat.com
State New
Headers show
Series [1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] | expand

Commit Message

Jonathan Wakely April 10, 2024, 8:45 a.m. UTC
Tested x86_64-linux.

Since this only affects C++26 it seems OK for trunk now.

-- >8 --

This C++26 change was just approved in Tokyo, in P2944R3. It adds
operator== and operator<=> overloads to std::reference_wrapper.

The operator<=> overloads in the paper cause compilation errors for any
type without <=> so they're implemented here with deduced return types
and constrained by a requires clause.

libstdc++-v3/ChangeLog:

	* include/bits/refwrap.h (reference_wrapper): Add comparison
	operators as proposed by P2944R3.
	* include/bits/version.def (reference_wrapper): Define.
	* include/bits/version.h: Regenerate.
	* include/std/functional: Enable feature test macro.
	* testsuite/20_util/reference_wrapper/compare.cc: New test.
---
 libstdc++-v3/include/bits/refwrap.h           | 45 +++++++++
 libstdc++-v3/include/bits/version.def         |  8 ++
 libstdc++-v3/include/bits/version.h           | 10 ++
 libstdc++-v3/include/std/functional           |  1 +
 .../20_util/reference_wrapper/compare.cc      | 95 +++++++++++++++++++
 5 files changed, 159 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc

Comments

Jonathan Wakely April 15, 2024, 6:30 p.m. UTC | #1
Pushed to trunk now.



On Wed, 10 Apr 2024 at 09:53, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Tested x86_64-linux.
>
> Since this only affects C++26 it seems OK for trunk now.
>
> -- >8 --
>
> This C++26 change was just approved in Tokyo, in P2944R3. It adds
> operator== and operator<=> overloads to std::reference_wrapper.
>
> The operator<=> overloads in the paper cause compilation errors for any
> type without <=> so they're implemented here with deduced return types
> and constrained by a requires clause.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/refwrap.h (reference_wrapper): Add comparison
>         operators as proposed by P2944R3.
>         * include/bits/version.def (reference_wrapper): Define.
>         * include/bits/version.h: Regenerate.
>         * include/std/functional: Enable feature test macro.
>         * testsuite/20_util/reference_wrapper/compare.cc: New test.
> ---
>  libstdc++-v3/include/bits/refwrap.h           | 45 +++++++++
>  libstdc++-v3/include/bits/version.def         |  8 ++
>  libstdc++-v3/include/bits/version.h           | 10 ++
>  libstdc++-v3/include/std/functional           |  1 +
>  .../20_util/reference_wrapper/compare.cc      | 95 +++++++++++++++++++
>  5 files changed, 159 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
>
> diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h
> index 2d4338b718f..fd1cc2b63e6 100644
> --- a/libstdc++-v3/include/bits/refwrap.h
> +++ b/libstdc++-v3/include/bits/refwrap.h
> @@ -38,6 +38,10 @@
>  #include <bits/invoke.h>
>  #include <bits/stl_function.h> // for unary_function and binary_function
>
> +#if __glibcxx_reference_wrapper >= 202403L // >= C++26
> +# include <compare>
> +#endif
> +
>  namespace std _GLIBCXX_VISIBILITY(default)
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
> @@ -358,6 +362,47 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type)
>  #endif
>           return std::__invoke(get(), std::forward<_Args>(__args)...);
>         }
> +
> +#if __glibcxx_reference_wrapper >= 202403L // >= C++26
> +      // [refwrap.comparisons], comparisons
> +      [[nodiscard]]
> +      friend constexpr bool
> +      operator==(reference_wrapper __x, reference_wrapper __y)
> +      requires requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
> +      { return __x.get() == __y.get(); }
> +
> +      [[nodiscard]]
> +      friend constexpr bool
> +      operator==(reference_wrapper __x, const _Tp& __y)
> +      requires requires { { __x.get() == __y } -> convertible_to<bool>; }
> +      { return __x.get() == __y; }
> +
> +      [[nodiscard]]
> +      friend constexpr bool
> +      operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
> +      requires (!is_const_v<_Tp>)
> +       && requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
> +      { return __x.get() == __y.get(); }
> +
> +      [[nodiscard]]
> +      friend constexpr auto
> +      operator<=>(reference_wrapper __x, reference_wrapper<_Tp> __y)
> +      requires requires { __detail::__synth3way(__x.get(), __y.get()); }
> +      { return __detail::__synth3way(__x.get(), __y.get()); }
> +
> +      [[nodiscard]]
> +      friend constexpr auto
> +      operator<=>(reference_wrapper __x, const _Tp& __y)
> +      requires requires { __detail::__synth3way(__x.get(), __y); }
> +      { return __detail::__synth3way(__x.get(), __y); }
> +
> +      [[nodiscard]]
> +      friend constexpr auto
> +      operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
> +      requires (!is_const_v<_Tp>)
> +       && requires { __detail::__synth3way(__x.get(), __y.get()); }
> +      { return __detail::__synth3way(__x.get(), __y.get()); }
> +#endif
>      };
>
>  #if __cpp_deduction_guides
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 5ad44941bff..5c0477fb61e 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1760,6 +1760,14 @@ ftms = {
>    };
>  };
>
> +ftms = {
> +  name = reference_wrapper;
> +  values = {
> +    v = 202403;
> +    cxxmin = 26;
> +  };
> +};
> +
>  ftms = {
>    name = saturation_arithmetic;
>    values = {
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 460a3e0116a..65e708c73fb 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -1963,6 +1963,16 @@
>  #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
>  #undef __glibcxx_want_ratio
>
> +#if !defined(__cpp_lib_reference_wrapper)
> +# if (__cplusplus >  202302L)
> +#  define __glibcxx_reference_wrapper 202403L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_reference_wrapper)
> +#   define __cpp_lib_reference_wrapper 202403L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_reference_wrapper) && defined(__glibcxx_want_reference_wrapper) */
> +#undef __glibcxx_want_reference_wrapper
> +
>  #if !defined(__cpp_lib_saturation_arithmetic)
>  # if (__cplusplus >  202302L)
>  #  define __glibcxx_saturation_arithmetic 202311L
> diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
> index 766558b3ce0..99364286a72 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -83,6 +83,7 @@
>  #define __glibcxx_want_move_only_function
>  #define __glibcxx_want_not_fn
>  #define __glibcxx_want_ranges
> +#define __glibcxx_want_reference_wrapper
>  #define __glibcxx_want_transparent_operators
>  #include <bits/version.h>
>
> diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
> new file mode 100644
> index 00000000000..039c9d26496
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
> @@ -0,0 +1,95 @@
> +// { dg-do compile { target c++26 } }
> +
> +
> +#include <functional>
> +
> +#ifndef __cpp_lib_reference_wrapper
> +# error "Feature-test macro for reference_wrapper missing"
> +#elif __cpp_lib_reference_wrapper != 202403
> +# error "Feature-test macro for reference_wrapper has wrong value"
> +#endif
> +
> +// P2944R3 Comparisons for reference_wrapper
> +
> +auto check(int i, std::reference_wrapper<int> r) -> bool {
> +  return i == r;
> +}
> +
> +template <class T> using Ref = std::reference_wrapper<T>;
> +
> +template <class T>
> +concept ref_equality_comparable
> += requires (T a, T const ca, Ref<T> r, Ref<T const> cr) {
> +    // the usual T is equality-comparable with itself
> +    a == a;
> +    a == ca;
> +    ca == ca;
> +
> +    // Ref<T> is equality-comparable with itself
> +    r == r;
> +    r == cr;
> +    cr == cr;
> +
> +    // T and Ref<T> are equality-comparable
> +    a == r;
> +    a == cr;
> +    ca == r;
> +    ca == cr;
> +};
> +
> +static_assert( ref_equality_comparable<int> );
> +
> +struct A {
> +    auto operator==(A const&) const -> bool { return true; }
> +};
> +
> +struct B {
> +    friend auto operator==(B const&, B const&) -> bool { return true; }
> +};
> +
> +template <class T>
> +struct C {
> +    friend auto operator==(C const&, C const&) -> bool { return true; }
> +};
> +
> +template <class T>
> +struct D { };
> +template <class T>
> +auto operator==(D<T> const&, D<T> const&) -> bool { return true; }
> +
> +static_assert(ref_equality_comparable<int>);
> +static_assert(ref_equality_comparable<A>);
> +static_assert(ref_equality_comparable<B>);
> +static_assert(ref_equality_comparable<C<int>>);
> +static_assert(ref_equality_comparable<D<int>>);
> +#include <string_view>
> +static_assert(ref_equality_comparable<std::string_view>);
> +
> +template <typename T>
> +struct ValArray {
> +  friend auto operator==(ValArray const&, ValArray const&) -> ValArray<bool> {
> +    return {};
> +  }
> +};
> +
> +void f(ValArray<int> v) {
> +  // this is valid and has type ValArray<bool>
> +  v == v;
> +
> +  // this is also valid today and has the same type
> +  std::ref(v) == std::ref(v);
> +}
> +
> +struct ComparesAsInt {
> +  friend auto operator==(ComparesAsInt, ComparesAsInt) -> int;
> +};
> +
> +auto f(std::reference_wrapper<ComparesAsInt> a,
> +    std::reference_wrapper<ComparesAsInt> b) {
> +  // today: compiles and returns int
> +  // proposed: compiles and returns bool
> +  return a == b;
> +}
> +
> +ComparesAsInt& c();
> +static_assert( std::is_same_v<decltype(f(c(), c())), bool> );
> --
> 2.44.0
>
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h
index 2d4338b718f..fd1cc2b63e6 100644
--- a/libstdc++-v3/include/bits/refwrap.h
+++ b/libstdc++-v3/include/bits/refwrap.h
@@ -38,6 +38,10 @@ 
 #include <bits/invoke.h>
 #include <bits/stl_function.h> // for unary_function and binary_function
 
+#if __glibcxx_reference_wrapper >= 202403L // >= C++26
+# include <compare>
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -358,6 +362,47 @@  _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type)
 #endif
 	  return std::__invoke(get(), std::forward<_Args>(__args)...);
 	}
+
+#if __glibcxx_reference_wrapper >= 202403L // >= C++26
+      // [refwrap.comparisons], comparisons
+      [[nodiscard]]
+      friend constexpr bool
+      operator==(reference_wrapper __x, reference_wrapper __y)
+      requires requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
+      { return __x.get() == __y.get(); }
+
+      [[nodiscard]]
+      friend constexpr bool
+      operator==(reference_wrapper __x, const _Tp& __y)
+      requires requires { { __x.get() == __y } -> convertible_to<bool>; }
+      { return __x.get() == __y; }
+
+      [[nodiscard]]
+      friend constexpr bool
+      operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+      requires (!is_const_v<_Tp>)
+	&& requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
+      { return __x.get() == __y.get(); }
+
+      [[nodiscard]]
+      friend constexpr auto
+      operator<=>(reference_wrapper __x, reference_wrapper<_Tp> __y)
+      requires requires { __detail::__synth3way(__x.get(), __y.get()); }
+      { return __detail::__synth3way(__x.get(), __y.get()); }
+
+      [[nodiscard]]
+      friend constexpr auto
+      operator<=>(reference_wrapper __x, const _Tp& __y)
+      requires requires { __detail::__synth3way(__x.get(), __y); }
+      { return __detail::__synth3way(__x.get(), __y); }
+
+      [[nodiscard]]
+      friend constexpr auto
+      operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+      requires (!is_const_v<_Tp>)
+	&& requires { __detail::__synth3way(__x.get(), __y.get()); }
+      { return __detail::__synth3way(__x.get(), __y.get()); }
+#endif
     };
 
 #if __cpp_deduction_guides
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 5ad44941bff..5c0477fb61e 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1760,6 +1760,14 @@  ftms = {
   };
 };
 
+ftms = {
+  name = reference_wrapper;
+  values = {
+    v = 202403;
+    cxxmin = 26;
+  };
+};
+
 ftms = {
   name = saturation_arithmetic;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 460a3e0116a..65e708c73fb 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1963,6 +1963,16 @@ 
 #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
 #undef __glibcxx_want_ratio
 
+#if !defined(__cpp_lib_reference_wrapper)
+# if (__cplusplus >  202302L)
+#  define __glibcxx_reference_wrapper 202403L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_reference_wrapper)
+#   define __cpp_lib_reference_wrapper 202403L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_reference_wrapper) && defined(__glibcxx_want_reference_wrapper) */
+#undef __glibcxx_want_reference_wrapper
+
 #if !defined(__cpp_lib_saturation_arithmetic)
 # if (__cplusplus >  202302L)
 #  define __glibcxx_saturation_arithmetic 202311L
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 766558b3ce0..99364286a72 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -83,6 +83,7 @@ 
 #define __glibcxx_want_move_only_function
 #define __glibcxx_want_not_fn
 #define __glibcxx_want_ranges
+#define __glibcxx_want_reference_wrapper
 #define __glibcxx_want_transparent_operators
 #include <bits/version.h>
 
diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
new file mode 100644
index 00000000000..039c9d26496
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
@@ -0,0 +1,95 @@ 
+// { dg-do compile { target c++26 } }
+
+
+#include <functional>
+
+#ifndef __cpp_lib_reference_wrapper
+# error "Feature-test macro for reference_wrapper missing"
+#elif __cpp_lib_reference_wrapper != 202403
+# error "Feature-test macro for reference_wrapper has wrong value"
+#endif
+
+// P2944R3 Comparisons for reference_wrapper
+
+auto check(int i, std::reference_wrapper<int> r) -> bool {
+  return i == r;
+}
+
+template <class T> using Ref = std::reference_wrapper<T>;
+
+template <class T>
+concept ref_equality_comparable
+= requires (T a, T const ca, Ref<T> r, Ref<T const> cr) {
+    // the usual T is equality-comparable with itself
+    a == a;
+    a == ca;
+    ca == ca;
+
+    // Ref<T> is equality-comparable with itself
+    r == r;
+    r == cr;
+    cr == cr;
+
+    // T and Ref<T> are equality-comparable
+    a == r;
+    a == cr;
+    ca == r;
+    ca == cr;
+};
+
+static_assert( ref_equality_comparable<int> );
+
+struct A {
+    auto operator==(A const&) const -> bool { return true; }
+};
+
+struct B {
+    friend auto operator==(B const&, B const&) -> bool { return true; }
+};
+
+template <class T>
+struct C {
+    friend auto operator==(C const&, C const&) -> bool { return true; }
+};
+
+template <class T>
+struct D { };
+template <class T>
+auto operator==(D<T> const&, D<T> const&) -> bool { return true; }
+
+static_assert(ref_equality_comparable<int>);
+static_assert(ref_equality_comparable<A>);
+static_assert(ref_equality_comparable<B>);
+static_assert(ref_equality_comparable<C<int>>);
+static_assert(ref_equality_comparable<D<int>>);
+#include <string_view>
+static_assert(ref_equality_comparable<std::string_view>);
+
+template <typename T>
+struct ValArray {
+  friend auto operator==(ValArray const&, ValArray const&) -> ValArray<bool> {
+    return {};
+  }
+};
+
+void f(ValArray<int> v) {
+  // this is valid and has type ValArray<bool>
+  v == v;
+
+  // this is also valid today and has the same type
+  std::ref(v) == std::ref(v);
+}
+
+struct ComparesAsInt {
+  friend auto operator==(ComparesAsInt, ComparesAsInt) -> int;
+};
+
+auto f(std::reference_wrapper<ComparesAsInt> a,
+    std::reference_wrapper<ComparesAsInt> b) {
+  // today: compiles and returns int
+  // proposed: compiles and returns bool
+  return a == b;
+}
+
+ComparesAsInt& c();
+static_assert( std::is_same_v<decltype(f(c(), c())), bool> );