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.

### 3746. `optional`'s spaceship with `U` with a type derived from `optional` causes infinite constraint meta-recursion

Section: 22.5.8 [optional.comp.with.t] Status: WP Submitter: Ville Voutilainen Opened: 2022-07-25 Last modified: 2022-11-17

Priority: Not Prioritized

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

View all issues with WP status.

Discussion:

What ends up happening is that the constraints of `operator<=>(const optional<T>&, const U&)` end up in `three_way_comparable_with`, and then in `partially-ordered-with`, and the expressions there end up performing a conversion from `U` to an `optional`, and we end up instantiating the same `operator<=>` again, evaluating its constraints again, until the compiler bails out.

See an online example here.

All implementations end up with infinite meta-recursion.

The solution to the problem is to stop the meta-recursion by constraining the spaceship with `U` so that `U` is not publicly and unambiguously derived from a specialization of optional, SFINAEing that candidate out, and letting 22.5.6 [optional.relops]/20 perform the comparison instead.

[2022-08-23; Reflector poll]

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

[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4910.

> [Drafting note: This proposed wording removes the only use of `is-optional`. ]

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

   >     […]
   >     namespace std {
   >       // 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
   >       template<class T>
   >         concept is-derived-from-optional = requires(const T& t) { // exposition only
   >           []<class U>(const optional<U>&){ }(t);
   >         };
   >       […]
   >       // 22.5.8 [optional.comp.with.t], comparison with T
   >       […]
   >       template<class T, class U> constexpr bool operator>=(const T&, const optional<U>&);
   >       template<class T, class U> requires (!is-derived-from-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, class U> requires (!is-derived-from-optional<U>) && three_way_comparable_with<T, U>
   >       constexpr compare_three_way_result_t<T, U>
   >         operator<=>(const optional<T>&, const U&);

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