Cppreference has the following description of mixed (optional and some other non-optional type) comparison operators for std::optional
:
Compares opt with a value. The values are compared (using the corresponding operator of T) only if opt contains a value. Otherwise, opt is considered less than value. If the corresponding two-way comparison expression between *opt and value is not well-formed, or if its result is not convertible to bool, the behavior is undefined.
What confuses me here is:
What would be examples of these not well-formed comparisons?
Why don't compilers/STL just reject the invalid comparisons instead of giving us UB?
The class template std::optional manages an optional contained value, i.e. a value that may or may not be present. A common use case for optional is the return value of a function that may fail.
What's more, std::optional doesn't need to allocate any memory on the free store. std::optional is a part of C++ vocabulary types along with std::any , std::variant and std::string_view .
October 4th, 20216. C++17 introduced std::optional<T> which lets you augment the values of a type T with a bonus value known as std::nullopt which semantically represents the absence of a value. A std::optional which holds the value std::nullopt is known as empty. The basic operations on std::optional are.
This stems from imprecise specification, which has since been rectified.
In C++17, these comparisons were specified as:
Requires: The expression
*x == *y
shall be well-formed and its result shall be convertible tobool
.
Requires was a precondition, so failing to meet these conditions would be undefined behavior. But there are many different kinds of "preconditions" - does it mean check this statically, does this mean remove the operator from the overload set if the conditions aren't met, does it mean actual undefined behavior?
In C++20, these are instead specified as:
Mandates: The expression
*x == *y
is well-formed and its result is convertible tobool
.
Which means that the program is ill-formed if the conditions aren't met. Basically, a mandate is a static_assert
(or equivalent).
So yes, the standard library is obligated to reject types whose comparison either doesn't exist or doesn't give you something like a bool
. This was never actually going to give you undefined behavior (what would an implementation do if it didn't have such an operator, read a bit from /dev/random
?) but now it's just much more clearly specified.
These changes came from a series of papers by Marshall Clow entitled "Mandating the Standard Library", this one specifically from P1460 (thanks, Marshall!). The new terminology for specifying the standard library comes from Walter Brown's "Guidelines for Formulating Library Semantics Specifications" paper (P1369).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With