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

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

Section: 22.5.9 [optional.comp.with.t] Status: New Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-09-15

Priority: Not Prioritized

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

View all issues with New 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
}

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;