- Title
- `std::optional` comparisons: constrain harder
- Status
- new
- Section
- [optional.comp.with.t]
- Submitter
- Jonathan Wakely

Created on **2024-04-19.00:00:00**
last changed **1 month ago**

Date: 2024-04-19.17:15:59

**Proposed resolution:**

This wording is relative to N4981.

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

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

-1-

*Constraints*: `U` is not a specialization of `optional`. The expression`*x == v`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator==(const T& v, const optional<U>& x);`

-3-

*Constraints*: `T` is not a specialization of `optional`. The expression`v == *x`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator!=(const optional<T>& x, const U& v);`

-5-

*Constraints*: `U` is not a specialization of `optional`. The expression`*x != v`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator!=(const T& v, const optional<U>& x);`

-7-

*Constraints*: `T` is not a specialization of `optional`. The expression`v != *x`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator<(const optional<T>& x, const U& v);`

-9-

*Constraints*: `U` is not a specialization of `optional`. The expression`*x < v`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator<(const T& v, const optional<U>& x);`

-11-

*Constraints*: `T` is not a specialization of `optional`. The expression`v < *x`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator>(const optional<T>& x, const U& v);`

-13-

*Constraints*: `U` is not a specialization of `optional`. The expression`*x > v`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator>(const T& v, const optional<U>& x);`

-15-

*Constraints*: `T` is not a specialization of `optional`. The expression`v > *x`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator<=(const optional<T>& x, const U& v);`

-17-

*Constraints*: `U` is not a specialization of `optional`. The expression`*x <= v`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator<=(const T& v, const optional<U>& x);`

-19-

*Constraints*: `T` is not a specialization of `optional`. The expression`v <= *x`

is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator>=(const optional<T>& x, const U& v);`

-21-

*Constraints*: `U` is not a specialization of `optional`. The expression *x >= v` is well-formed and its result is convertible to `bool`.`template<class T, class U> constexpr bool operator>=(const T& v, const optional<U>& x);`

-23-

*Constraints*: `T` is not a specialization of `optional`. The expression`v >= *x`

is well-formed and its result is convertible to `bool`.

Date: 2024-04-22.09:23:50

p2944r3 added constraints to `std::optional`'s comparisons, e.g.

-1-`template<class T, class U> constexpr bool operator==(const optional<T>& x, const optional<U>& y);`

: The expression `*x == *y` is well-formed and its result is convertible to `bool`.~~Mandates~~Constraints…

-1-`template<class T, class U> constexpr bool operator==(const optional<T>& x, const U& v);`

: The expression `*x == v` is well-formed and its result is convertible to `bool`.~~Mandates~~Constraints

But I don't think the constraint on the second one (the "compare with value")
is correct. If we try to compare two optionals that can't be compared,
such as `optional<void*>`

and `optional<int>`

,
then the first overload is not valid due to the new constraints, and so does
not participate in overload resolution. But that means we now consider the
second overload, but that's ambiguous. We could either use
`operator==<void*, optional<int>>`

or we could use
`operator==<optional<void*>, int>`

with the arguments
reversed (using the C++20 default comparison rules).
We never even get as far as checking the new constraints on those overloads,
because they're simply ambiguous.

Before p2944r3 overload resolution always would have selected the first overload, for comparing two optionals. But because that is now constrained away, we consider an overload that should never be used for comparing two optionals. The solution is to add an additional constraint to the "compare with value" overloads so that they won't be used when the "value" is really another optional.

A similar change was made to `optional`'s `operator<=>`

by
LWG 3566, and modified by LWG 3746.
I haven't analyzed whether we need the modification here too.

The proposed resolution (without *`is-derived-from-optional`*)
has been implemented and tested in libstdc++.

History | |||
---|---|---|---|

Date | User | Action | Args |

2024-04-19 17:15:59 | admin | set | messages: + msg14068 |

2024-04-19 00:00:00 | admin | create |