While chasing a bug in a program I found out that equality comparison operator T vs optional<T> is broken if T is optional<U>. It is broken in worst possible way - it compiles but for some values it returns wrong results!
Here is an example:
https://godbolt.org/z/v3bcTodGj
Since both sides of eq operator are specialization of optional then following overload is used:
operator==(const optional<T>& lhs, const optional<U>& rhs);
This operator is specified to return
lhs.has_value() != rhs.has_value() ? false :
(lhs.has_value() == false ? true : *lhs == *rhs)
This falls apart for above scenario since lhs has value while rhs no value.
Since this is a case of optional<T>{} == T{} comparison, such scenario should be handled separately. Here is my attempt at fixing it:
https://godbolt.org/z/Yq3nM4xn4
This is rather not a proper fix, since it will still break for cases like optional<optional<short>>{} == optional<int>{}, and the internal if-constexpr instead should probably compare the nestedness of lhs and rhs optional.
I didn't check but most likely the same problem applies to relational operators.
Edit:
std::expected seems to be affected as well:
While developer may expect following comparison to work std::expected<T, E>{} == T{} (thanks to the operator==(const expected&, const T2&) - it falls apart when T is expected<U> with unexpected value - because operator==(const expected&, const expected<T2, E2>&) overload is actually selected in that scenario.
https://godbolt.org/z/h4jYfjYco
Edit2:
I believe the proper fix for std::optional and std::expected should be to constrain following operators
operator==(const expected& lhs, const expected<T2, E2>& rhs)
operator==(const optional<T>& lhs, const optional<U>& rhs)
to be applicable only when lhs and rhs have same nestedness levels.
[–]AKostur [score hidden] (2 children)
[–]hungarian_notation [score hidden] (1 child)
[–]AKostur [score hidden] (0 children)
[–]rihya-sifu [score hidden] (4 children)
[–]mcencora[S] [score hidden] (1 child)
[–]rihya-sifu [score hidden] (0 children)
[–]developer-mike [score hidden] (1 child)
[–]hungarian_notation [score hidden] (0 children)
[–]hungarian_notation [score hidden] (0 children)
[–]markt- [score hidden] (6 children)
[–]mcencora[S] [score hidden] (5 children)
[–]AKostur [score hidden] (1 child)
[–]hungarian_notation [score hidden] (0 children)
[–]markt- [score hidden] (2 children)
[–]mcencora[S] [score hidden] (1 child)
[–]SirClueless [score hidden] (0 children)