Issue 4366: Heterogeneous comparison of expected may be ill-formed (original) (raw)


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.

4366. Heterogeneous comparison of expected may be ill-formed

Section: 22.8.6.8 [expected.object.eq], 22.8.7.8 [expected.void.eq] Status: WP Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-11-11

Priority: Not Prioritized

View all issues with WP status.

Discussion:

These comparison functions all explicitly static_cast the result of the underlying comparison tobool. 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

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.

[2025-10-16; Reflector poll]

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

Related to LWG 4366(i), but the wording styles are inconsistent.optional uses "Effects: Equivalent to ..." and expected just uses_Returns_:.

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

Proposed resolution:

This wording is relative to N5014.

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

    template 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 friend constexpr bool operator==(const expected& x, const unexpected& 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 22.8.7.8 [expected.void.eq] as indicated:

    template<class T2, class E2> requires is_void_v
    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
    friend constexpr bool operator==(const expected& x, const unexpected& 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.