Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In which cases does std::optional operator == cause undefined behavior?

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?

like image 590
NoSenseEtAl Avatar asked Jun 23 '20 12:06

NoSenseEtAl


People also ask

What is the use of std :: optional?

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.

Does STD optional allocate memory?

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 .

What is Nullopt?

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.


1 Answers

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 to bool.

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 to bool.

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).

like image 184
Barry Avatar answered Nov 14 '22 22:11

Barry