Title
Heterogeneous comparison of `expected` may be ill-formed
Status
wp
Section
[expected.object.eq][expected.void.eq]
Submitter
Hewill Kang

Created on 2025-09-06.00:00:00 last changed 3 weeks ago

Messages

Date: 2025-11-11.10:48:16

Proposed resolution:

This wording is relative to N5014.

  1. Modify [expected.object.eq] as indicated:

    template<class T2> friend constexpr bool operator==(const expected& x, const T2& v);
    

    -3- Constraints: `T2` is not a specialization of `expected`. The expression `*x == v` is well-formed and its result is convertible to `bool`.

    [Note 1: T need not be Cpp17EqualityComparable. — end note]

    -4- Returns: If `x.has_value()` is `true`, && static_cast<bool>(*x == v); otherwise `false`.

    template<class E2> friend constexpr bool operator==(const expected& x, const unexpected<E2>& e);
    

    -5- Constraints: The expression `x.error() == e.error()` is well-formed and its result is convertible to `bool`.

    -6- Returns: If `!x.has_value()` is `true`, && static_cast<bool>(x.error() == e.error()); otherwise `false`.

  2. Modify [expected.void.eq] as indicated:

    template<class T2, class E2> requires is_void_v<T2>
      friend constexpr bool operator==(const expected& x, const expected<T2, E2>& y);
    

    -1- Constraints: The expression `x.error() == y.error()` is well-formed and its result is convertible to `bool`.

    -2- Returns: If `x.has_value()` does not equal `y.has_value()`, `false`; otherwise if `x.has_value()` is `true`, `true`; otherwise || static_cast<bool>(x.error() == y.error()).

    template<class E2>
      friend constexpr bool operator==(const expected& x, const unexpected<E2>& e);
    

    -3- Constraints: The expression `x.error() == e.error()` is well-formed and its result is convertible to `bool`.

    -4- Returns: If `!x.has_value()` is `true`, && static_cast<bool>(x.error() == e.error()) ; otherwise `false`.

Date: 2025-11-11.10:48:16

[ Kona 2025-11-08; Status changed: Voting → WP. ]

Date: 2025-10-15.00:00:00

[ 2025-10-16; Reflector poll ]

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

Related to LWG 4366, but the wording styles are inconsistent. `optional` uses "Effects: Equivalent to ..." and `expected` just uses Returns:.

Date: 2025-09-06.00:00:00

These comparison functions all explicitly `static_cast` the result of the underlying comparison to `bool`. However, the Constraints only require the implicit conversion, not the explicit one (i.e., "convertible to `bool`" rather than "models boolean-testable").

This means that in some pathological cases it will lead to hard errors (demo):

#include <expected>

struct E1 {};
struct E2 {};

struct Bool {
  operator bool() const;
  explicit operator bool() = delete;
};
Bool operator==(E1, E2);

int main() {
  std::unexpected e1{E1{}};
  std::unexpected e2{E2{}};
  return std::expected<int, E1>{e1} == e2; // fire
}

It is reasonable to specify return consistency with actual Constraints.

History
Date User Action Args
2025-11-11 10:48:16adminsetmessages: + msg15643
2025-11-11 10:48:16adminsetstatus: voting -> wp
2025-10-30 17:45:31adminsetstatus: ready -> voting
2025-10-16 16:27:52adminsetmessages: + msg15193
2025-10-16 16:27:52adminsetstatus: new -> ready
2025-09-15 13:04:02adminsetmessages: + msg15049
2025-09-06 00:00:00admincreate