C++2b(C++23)標準ライブラリに追加されるstd::expected<T, E>
の例外安全性についてメモ。
正常型T
/エラー型E
を値(value)として取り扱いつつ「強い例外安全性(Strong exception safety)」を満たすために、テンプレートパラメータに一定の制約が課される。
- ムーブ/コピー代入:
T
もしくはE
のいずれかが “nothrowムーブ構築可能” であること - 正常型
U
代入:T
が “nothrow構築可能” または、T
もしくはE
のいずれかが “nothrowムーブ構築可能” であること - エラー型
G
代入:E
が “nothrow構築可能” または、T
もしくはE
のいずれかが “nothrowムーブ構築可能” であること swap
:T
もしくはE
のいずれかが “nothrowムーブ構築可能” であること
T
型/E
型の少なくとも一方でnothrow保証のある構築手段を確保し、他方の型において例外送出された場合は操作前状態までロールバック処理*1を行う(後述引用のcatch
節を参照)。
C++2b DIS N4950 22.8.6.4/p1-2, 22.8.6.5/p2より引用。各種代入演算子(operator=
)の効果は説明用reinit-expected
関数を用いて規定される。
1 This subclause makes use of the following exposition-only function:
template<class T, class U, class... Args> constexpr void reinit-expected(T& newval, U& oldval, Args&&... args) { // exposition only if constexpr (is_nothrow_constructible_v<T, Args...>) { destroy_at(addressof(oldval)); construct_at(addressof(newval), std::forward<Args>(args)...); } else if constexpr (is_nothrow_move_constructible_v<T>) { T tmp(std::forward<Args>(args)...); destroy_at(addressof(oldval)); construct_at(addressof(newval), std::move(tmp)); } else { U tmp(std::move(oldval)); destroy_at(addressof(oldval)); try { construct_at(addressof(newval), std::forward<Args>(args)...); } catch (...) { construct_at(addressof(oldval), std::move(tmp)); throw; } } }constexpr expected& operator=(const expected& rhs);2 Effects:
- If
this->has_value() && rhs.has_value()
istrue
, equivalent toval = *rhs
.- Otherwise, if
this->has_value()
istrue
, equivalent to:reinit-expected(unex, val, rhs.error())
- Otherwise, if
rhs.has_value()
istrue
, equivalent to:reinit-expected(val, unex, *rhs)
- Otherwise, equivalent to
unex = rhs.error()
.Then, if no exception was thrown, equivalent to:
has_val = rhs.has_value(); return *this;
constexpr void swap(expected& rhs) noexcept(see below);2 Effects: See Table 63.
Table 63:
swap(expected&)
effects
this->has_value()
!this->has_value()
rhs.has_value()
equivalent to: using std::swap; swap(val, rhs.val);
calls rhs.swap(*this)
!rhs.has_value()
see below equivalent to: using std::swap; swap(unex, rhs.unex);
For the case where
rhs.value()
isfalse
andthis->has_value()
istrue
, equivalent to:if constexpr (is_nothrow_move_constructible_v<E>) { E tmp(std::move(rhs.unex)); destroy_at(addressof(rhs.unex)); try { construct_at(addressof(rhs.val), std::move(val)); destroy_at(addressof(val)); construct_at(addressof(unex), std::move(tmp)); } catch(...) { construct_at(addressof(rhs.unex), std::move(tmp)); throw; } } else { T tmp(std::move(val)); destroy_at(addressof(val)); try { construct_at(addressof(unex), std::move(rhs.unex)); destroy_at(addressof(rhs.unex)); construct_at(addressof(rhs.val), std::move(tmp)); } catch (...) { construct_at(addressof(val), std::move(tmp)); throw; } } has_val = false; rhs.has_val = true;
関連URL
- std::expected<T, E> - yohhoyの日記
- cppreference std::expected
- cpprefjp std::expected