diff --git a/2944_comparisons_for_reference_wrapper/comparisons-for-reference_wrapper.md b/2944_comparisons_for_reference_wrapper/comparisons-for-reference_wrapper.md
index 0c149157..e6fd4c03 100644
--- a/2944_comparisons_for_reference_wrapper/comparisons-for-reference_wrapper.md
+++ b/2944_comparisons_for_reference_wrapper/comparisons-for-reference_wrapper.md
@@ -11,7 +11,7 @@ toc: true
# Revision History
-Since [@P2944R1], added section on [ambiguity](#ambiguity) and updated wording accordingly.
+Since [@P2944R1], added section on [ambiguity](#ambiguity-issues) and updated wording accordingly.
Since [@P2944R0], fixed the wording
@@ -103,7 +103,7 @@ if (std::ranges::any_of(v, equals(std::ref(target)))) {
And this works! Just... only for some types, seemingly randomly. The goal of this proposal is for it to just always work.
-## Ambiguity
+## Ambiguity Issues
In the original revision of the paper, the proposal was simply to add this equality operator:
@@ -179,6 +179,69 @@ template Since [P2944R1], added section on ambiguity and updated wording accordingly. Since [P2944R1], added section on ambiguity and updated wording accordingly. Since [P2944R0], fixed the wording Typically in libraries, wrapper types are comparable when their underlying types are comparable. And this works! Just… only for some types, seemingly randomly. The goal of this proposal is for it to just always work. In the original revision of the paper, the proposal was simply to add this equality operator: And that, now, passes all the tests. Another question that came up with in the LEWG telecon was how this proposal interacts with non-boolean comparison operators. For instance: Now, Now, does anybody write such code? Who knows. If we constrain the comparisons of But, as always, there is an edge case. Then the comparisons to Here, the added comparison operators would be valid, and wouldn’t constrain away, since This would be the only case where any behavior would change. Add Change 22.10.6.1 [refwrap.general]: Add a new clause, [refwrap.comparisons], after 22.10.6.5 [refwrap.invoke]: 1 Mandates: The expression 2 Returns: 3 Mandates: The expression 4 Returns: 5 Constraints: 6 Mandates: The expression 7 Returns: 8 Returns: 9 Returns: 10 Constraints: 11 Returns: Contents
@@ -577,7 +578,7 @@
Contents
1 Revision History
-2 Introduction
tuple<T>
is equality comparable when T
is. optional<T>
is equality comparable when T
is. variant<T>
is equality comparable when T
is.}
2.1 Ambiguity
+2.1 Ambiguity Issues
2.2 Non-boolean comparisons
+
+
+
+std::valarray<T>
’s comparison operators are specified as non-member function templates, so any comparison using std::reference_wrapper<std::valarray<T>>
doesn’t work today. But let’s make our own version of this type that’s more friendly (or hostile, depending on your perspective) to this paper and consider:
+
+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);
+}
std::reference_wrapper<T>
(and also the other standard library types), then this code will continue to work fine anyway - since the comparisons would be constrained away by types like ValArray<T>
not satisfying equality_comparable
. This paper would not be adding any new candidates to the candidate set, so no behavior changes.
+
+T
, whose comparisons return a type like int
T
to std::reference_wrapper<T>
works (see table above)int
std::reference_wrapper<T>
will instead start returning bool
. That is:
+
+
+std::equality_comparable
is based on boolean-testable
which only requires convertibility to bool
(and some other nice behavior), which int
does satisfy. And those added comparison operators would be better matches than the existing ones, so they would win.3 Proposal
==
and <=>
to std::reference_wrapper<T>
so that std::reference_wrapper<T>
is always comparable when T
is, regardless of how T
’s comparisons are defined.
template<class T> class reference_wrapper {
- public:
- // types
- using type = T;
-
- // [refwrap.const], constructors
- template<class U>
- constexpr reference_wrapper(U&&) noexcept(see below);
- constexpr reference_wrapper(const reference_wrapper& x) noexcept;
-
- // [refwrap.assign], assignment
- constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;
-
- // [refwrap.access], access
- constexpr operator T& () const noexcept;
- constexpr T& get() const noexcept;
-
- // [refwrap.invoke], invocation
- template<class... ArgTypes>
- constexpr invoke_result_t<T&, ArgTypes...> operator()(ArgTypes&&...) const
- noexcept(is_nothrow_invocable_v<T&, ArgTypes...>);
-
-+ // [refwrap.comparisons], comparisons
-+ friend constexpr bool operator==(reference_wrapper, reference_wrapper);
-+ friend constexpr bool operator==(reference_wrapper, const T&);
-+ friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>);
-
-+ friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper);
-+ friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, const T&);
-+ friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper<const T>);
- };
template<class T> class reference_wrapper {
+ public:
+ // types
+ using type = T;
+
+ // [refwrap.const], constructors
+ template<class U>
+ constexpr reference_wrapper(U&&) noexcept(see below);
+ constexpr reference_wrapper(const reference_wrapper& x) noexcept;
+
+ // [refwrap.assign], assignment
+ constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;
+
+ // [refwrap.access], access
+ constexpr operator T& () const noexcept;
+ constexpr T& get() const noexcept;
+
+ // [refwrap.invoke], invocation
+ template<class... ArgTypes>
+ constexpr invoke_result_t<T&, ArgTypes...> operator()(ArgTypes&&...) const
+ noexcept(is_nothrow_invocable_v<T&, ArgTypes...>);
+
++ // [refwrap.comparisons], comparisons
++ friend constexpr bool operator==(reference_wrapper, reference_wrapper);
++ friend constexpr bool operator==(reference_wrapper, const T&);
++ friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>);
+
++ friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper);
++ friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, const T&);
++ friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper<const T>);
+ };
x.get() == y.get()
is well-formed and its result is convertible to bool
.x.get() == y.get()
.x.get() == y
is well-formed and its result is convertible to bool
.x.get() == y
.is_const_v<T>
is false
.x.get() == y.get()
is well-formed and its result is convertible to bool
.x.get() == y.get()
.synth-three-way(x.get(), y.get())
.synth-three-way(x.get(), y)
.friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper x, reference_wrapper<const T> y);
friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper x, reference_wrapper<const T> y);
is_const_v<T>
is false
.synth-three-way(x.get(), y.get())
.std::reference_wrapper<T>, and there doesn’t seem like a good one to bump for this, so let’s add a new one to 17.3.2 [version.syn]