This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3566. Constraint recursion for `operator<=>(optional<T>, U)`

Section: 22.5.8 [optional.comp.with.t] Status: WP Submitter: Casey Carter Opened: 2021-06-07 Last modified: 2021-10-14

Priority: Not Prioritized

View all other issues in [optional.comp.with.t].

View all issues with WP status.

Discussion:

The three-way-comparison operator for optionals and non-optionals is defined in [optional.comp.with.t] paragraph 25:

>     template<class T, three_way_comparable_with<T> U>
>       constexpr compare_three_way_result_t<T, U>
>         operator<=>(const optional<T>& x, const U& v);

> > -25- Effects: Equivalent to: `return bool(x) ? *x <=> v : strong_ordering::less;`

Checking `three_way_comparable_with` in particular requires checking that `x < v` is well-formed for an lvalue `const optional<T>` and lvalue `const U`. `x < v` can be rewritten as `(v <=> x) > 0`. If `U` is a specialization of `optional`, this overload could be used for `v <=> x`, but first we must check the constraints…

The straightforward fix for this recursion seems to be to refuse to check `three_way_comparable_with<T>` when `U` is a specialization of `optional`. MSVC has been shipping such a fix; our initial tests for this new operator triggered the recursion.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4885.

> 1. Modify 22.5.2 [optional.syn], header `<optional>` synopsis, as indicated:

>    >     […]
>    >     // 22.5.8 [optional.comp.with.t], comparison with T
>    >     […]
>    >     template<class T, class U> constexpr bool operator>=(const optional<T>&, const U&);
>    >     template<class T, class U> constexpr bool operator>=(const T&, const optional<U>&);
>    >     template<class T, three_way_comparable_with<T>class U>
>    >       requires see below
>    >         constexpr compare_three_way_result_t<T, U>
>    >           operator<=>(const optional<T>&, const U&);

>    >     […]

> 2. Modify 22.5.8 [optional.comp.with.t] as indicated:

>    >     template<class T, three_way_comparable_with<T>class U>
>    >       requires (!is-specialization-of<U, optional>) && three_way_comparable_with<T, U>
>    >         constexpr compare_three_way_result_t<T, U>
>    >           operator<=>(const optional<T>&, const U&);

>    > > -?- The exposition-only trait template `is-specialization-of<A, B>` is a constant expression with value `true` when `A` denotes a specialization of the class template `B`, and `false` otherwise.

>    > > -25- Effects: Equivalent to: `return bool(x) ? *x <=> v : strong_ordering::less;`

[2021-06-14; Improved proposed wording based on reflector discussion]

[2021-06-23; Reflector poll]

Set status to Tentatively Ready after five votes in favour during reflector poll.

[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4885.

1. Modify 22.5.2 [optional.syn], header `<optional>` synopsis, as indicated:

   >     […]
   >     // 22.5.3 [optional.optional], class template optional
   >     template<class T>
   >     class optional;

   >     template<class T>
   >       constexpr bool is-optional = false;               // exposition only
   >     template<class T>
   >       constexpr bool is-optional<optional<T>> = true;   // exposition only

   >     […]
   >     // 22.5.8 [optional.comp.with.t], comparison with T
   >     […]
   >     template<class T, class U> constexpr bool operator>=(const optional<T>&, const U&);
   >     template<class T, class U> constexpr bool operator>=(const T&, const optional<U>&);
   >     template<class T, three_way_comparable_with<T>class U>
   >       requires (!is-optional<U>) && three_way_comparable_with<T, U>
   >         constexpr compare_three_way_result_t<T, U>
   >           operator<=>(const optional<T>&, const U&);

   >     […]

2. Modify 22.5.8 [optional.comp.with.t] as indicated:

   >     template<class T, three_way_comparable_with<T>class U>
   >       requires (!is-optional<U>) && three_way_comparable_with<T, U>
   >         constexpr compare_three_way_result_t<T, U>
   >           operator<=>(const optional<T>& x, const U& v);

   > > -25- Effects: Equivalent to: `return bool(x) ? *x <=> v : strong_ordering::less;`