Does anything prevent std::optional::value_or() from being conditionally noexcept?
Asked Answered
S

1

9

Here's the definition of value_or() from the C++17 standard:

template <class U> constexpr T value_or(U&& v) const&;

Effects: Equivalent to:

return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));

Remarks: If is_copy_constructible_v<T> && is_convertible_v<U&&, T> is false, the program is ill-formed.

(the rvalue overload is similar)

The effect of value_or is described as equivalent to return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));

operator bool is noexcept. operator* is not noexcept (even though it doesn't throw, probably because it can still fail with UB if used when the optional does not contain a value). However, we are guaranteed to never try to return the contained value unless we have one.

So couldn't value_or be declared noexcept given is_nothrow_copy_constructible<T> && noexcept(static_cast<T>(std::forward<U>(v)))?

Sigmoid answered 3/5, 2018 at 18:53 Comment(2)
Conditional noexcept isn't heavily used in the C++ standard might "count" as something that prevents it being used here.Oceanid
@Yakk I'd think this answers the posed question, but another one arises immediately: why isn't it widely used?Gratuity
O
7

is used extremely sparingly in the standard. That is about the only barrier.


While this answers the question, the next question is "why is it used sparingly". This is an extra bit of information you might find useful; if this was the core of the answer, I'd include more quotes instead of links. The paper numbers should outlive the specific hyperlinks I'm using, so there is that.

N3279 is the conclusion of a discussion about noexcept. Basically, anything with a narrow contract (that can exhibit UB) and isn't a move ctor or dtor is never marked noexcept.

Here are the guidelines:

Adopted Guidelines

  • No library destructor should throw. They shall use the implicitly supplied (non-throwing) exception specification.
  • Each library function having a wide contract, that the LWG agree cannot throw, should be marked as unconditionally noexcept.
  • If a library swap function, move-constructor, or move-assignment operator is conditionally-wide (i.e. can be proven to not throw by applying the noexcept operator) then it should be marked as conditionally noexcept. No other function should use a conditional noexcept specification.
  • Library functions designed for compatibility with “C” code (such as the atomics facility), may be marked as unconditionally noexcept.

I wasn't party to the discussion, but basically the idea is that the compiler can add exceptions to those methods.

I believe this is called the Lakos rule. To change it, take it up with the committee.

N3248 is the paper that brought up issues with noexcept. Mostly it is about testing.

Oceanid answered 3/5, 2018 at 19:38 Comment(7)
Relevant guideline to this question is on page 2: only swap() and move constructorsassignments in the library are conditionally noexcept.Vacillating
At the previous committee meeting, I think that they agreed to make "wrapper types" conditionally noexcept based on the operations of the types they wrap. std::optional and std::variant would be such wrapper types.Kingwood
This is a good answer for any "Why is X not noexcept" questionPattipattie
@morwenn do you have documentation or links to that effect so I can insert a quote with attribution?Oceanid
@Kingwood what do you mean by making the type conditionally noexcept? Are you referring to any particular operations on them in particular? In c++17, optional(optional&& rhs) is already noexcept if is_nothrow_move_constructible_v<T> (and similar for move assignment). variant does the same, but it has to hold for all the wrapped types.Sigmoid
@Yakk The paper I was mentioning was [P0884 - Extending the noexcept Policy](wg21.link/P0884], which is actually rather terse. The paper cites std::atomic and the proposed std::function_ref as examples of wrapping types. I just checked the meeting minutes (non public) and the question was raised about std::optional and std::variant but the actual answer was that the aim of the paper was to broaden the conditional noexcept policy not to make firm decisions, so no definite answer for those types. Apparently the committee was strongly for accepting this paper.Kingwood
@Sigmoid Just rechecked the paper again and it actually only proposes conditional noexcept for the default constructor, copy constructor and copy assignment operator of wrapper types, so unlike what I thought there is no way it affects value_or, sorry :/Kingwood

© 2022 - 2025 — McMap. All rights reserved.