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

4370. Comparison of optional<T> to T may be ill-formed

Section: 22.5.9 [optional.comp.with.t] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-10-16

Priority: Not Prioritized

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

View all issues with Tentatively Ready status.

Discussion:

When comparing an optional with its value type, the current wording specifies that the result is the ternary expression of x.has_value() ? *x == v : false, where *x == v returns a result that can be implicitly converted to bool.

However, when the result can also be constructed using bool (which is common), the ternary operation will be ill-formed due to ambiguity (demo):

#include <optional>

struct Bool {
  Bool(bool);
  operator bool() const;
};

struct S {
  Bool operator==(S) const;
};

int main() {
  return std::optional<S>{} == S{}; // fire
}

[2025-10-16; Reflector poll]

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

"Alternatively could keep the conditional operator but cast one side to bool, but that would do an explicit conversion, which might not be what we want."

"Should just require boolean-testable."

Related to LWG 4366(i).

Proposed resolution:

This wording is relative to N5014.

  1. Modify 22.5.9 [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.

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

    -2- Effects: Equivalent to: return x.has_value() ? *x == v : false;

    if (x.has_value())
      return *x == v;
    return false;
    
    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.

    -4- Effects: Equivalent to: return x.has_value() ? v == *x : false;

    if (x.has_value())
      return v == *x;
    return false;
    
    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.

    -6- Effects: Equivalent to: return x.has_value() ? *x != v : true;

    if (x.has_value())
      return *x != v;
    return true;
    
    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.

    -8- Effects: Equivalent to: return x.has_value() ? v != *x : true;

    if (x.has_value())
      return v != *x;
    return true;
    
    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.

    -10- Effects: Equivalent to: return x.has_value() ? *x < v : true;

    if (x.has_value())
      return *x < v;
    return true;
    
    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.

    -12- Effects: Equivalent to: return x.has_value() ? v < *x : false;

    if (x.has_value())
      return v < *x;
    return false;
    
    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.

    -14- Effects: Equivalent to: return x.has_value() ? *x > v : false;

    if (x.has_value())
      return *x > v;
    return false;
    
    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.

    -16- Effects: Equivalent to: return x.has_value() ? v > *x : true;

    if (x.has_value())
      return v > *x;
    return true;
    
    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.

    -18- Effects: Equivalent to: return x.has_value() ? *x <= v : true;

    if (x.has_value())
      return *x <= v;
    return true;
    
    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.

    -20- Effects: Equivalent to: return x.has_value() ? v <= *x : false;

    if (x.has_value())
      return v <= *x;
    return false;
    
    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.

    -22- Effects: Equivalent to: return x.has_value() ? *x >= v : false;

    if (x.has_value())
      return *x >= v;
    return false;
    
    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.

    -24- Effects: Equivalent to: return x.has_value() ? v >= *x : true;

    if (x.has_value())
      return v >= *x;
    return true;