Exception specifications of copy/move member functions of std::bad_expected_access
[expected.bad][expected.bad.void] [expected.object.obs][expected.void.obs]
Jiang An

Created on 2022-03-24.00:00:00 last changed 3 months ago


Date: 2023-05-15.00:00:00

[ 2023-05-25; Jonathan comments ]

The last part was clarified by LWG 3843, confirming that value() is ill-formed for move-only E.

Date: 2022-05-15.00:00:00

[ 2022-05-17; Reflector poll ]

Set priority to 2 after reflector poll.

Date: 2022-03-26.15:02:03

The move constructor and the move assignment operator of standard exception types are not covered by [exception]/2, and thus it is currently effectively unspecified whether these move functions of std::bad_expected_access<void> are noexcept. Furthermore, std::bad_expected_access<void> has protected special member functions, which overrides (or conflicts with?) the general rule in [exception]/2.

The primary template std::bad_expected_access<E> stores an E object, and copying the stored object may throw an exception. Is it intended that the copy functions of std::bad_expected_access<E> are noexcept while those of E are not? When the copy happens because a std::bad_expected_access<E> is caught by value, if the copy constructor of E throws, std::terminate is called no matter whether that of std::bad_expected_access<E> is noexcept. But std::bad_expected_access<E> may be copied/moved in other circumstances.

I think the move constructor and the move assignment operator of a standard exception type should be specified to be public and noexcept when they exist, although sometimes whether they exist may be unspecified. And the move functions should also propagate the result of what() when the source and target have the same dynamic type, except that they can leave the result of what() from the source valid but unspecified.

Related to this, std::expected<T, E>::value overloads are specified to throw std::bad_expected_access<std::decay_t<E>> when an E is contained. However, it seems that the copy constructor of std::bad_expected_access is implicitly deleted if E is move-only, so the throw-expression is ill-formed.

Is it intended that std::expected<T, E>::value is ill-formed in such cases?

Date User Action Args
2023-05-25 08:41:21adminsetmessages: + msg13580
2022-05-17 11:58:16adminsetmessages: + msg12461
2022-03-24 00:00:00admincreate