reference_wrapper
comparisons are not SFINAE-friendlySection: 22.10.6.6 [refwrap.comparisons] Status: WP Submitter: Jonathan Wakely Opened: 2024-04-19 Last modified: 2024-07-08
Priority: Not Prioritized
View all issues with WP status.
Discussion:
P2944R3 added these hidden friends to reference_wrapper
:
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>);
These functions are not templates, and so their declarations are ill-formed for any type that does have any comparison operators, e.g.
struct A { } a;
std::reference_wrapper<A> r(a);
Instantiating reference_wrapper<A>
will instantiate
the declarations of the hidden friends, which will attempt to determine the
return types of the operator<=>
functions.
That fails because synth-three-way
is constrained
and can't be called with arguments of type A
.
This can be solved by changing those functions into templates, so they aren't instantiated eagerly, e.g.,
template<class U = T>
friend constexpr synth-three-way-result<TU> operator<=>(reference_wrapper, reference_wrapper);
or by giving them a deduced return type (so that it isn't instantiated eagerly)
and constraining them to only be callable when valid:
friend constexpr synth-three-way-result<T>auto operator<=>(reference_wrapper x, reference_wrapper y)
requires requires (const T t) { synth-three-way(t, t); }
The second alternative is used in the proposed resolution.
In practice the requires-clause can be implemented more simply (and efficiently) by checking the constraints of synth-three-way directly:
requires (const T t) { { t < t } -> boolean-testable; }
but when specified in prose in a Constraints: element it seems
clearer to just use synth-three-way(x.get(), y.get())
.
The proposed resolution has been committed to libstdc++'s master branch.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
[St. Louis 2024-06-29; Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4981.
Modify 22.10.6.1 [refwrap.general] as indicated:
// [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>auto operator<=>(reference_wrapper, reference_wrapper); friend constexprsynth-three-way-result<T>auto operator<=>(reference_wrapper, const T&); friend constexprsynth-three-way-result<T>auto operator<=>(reference_wrapper, reference_wrapper<const T>);
Modify 22.10.6.6 [refwrap.comparisons] as indicated:
friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper x, reference_wrapper y);-?- Constraints: The expression
synth-three-way(x.get(), y.get())
is well-formed.-7- Returns:
synth-three-way(x.get(), y.get())
.friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper x, const T& y);-?- Constraints: The expression
synth-three-way(x.get(), y)
is well-formed.-8- Returns:
synth-three-way(x.get(), y)
.friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper x, reference_wrapper<const T> y);-9- Constraints:
is_const_v<T>
isfalse
. The expressionsynth-three-way(x.get(), y.get())
is well-formed.-10- Returns:
synth-three-way(x.get(), y.get())
.