From 724b29c83cfdd8bddf4cdc1bb3c657b6c9e17d4c Mon Sep 17 00:00:00 2001 From: yoh Date: Sun, 29 Jan 2023 23:29:20 +0900 Subject: [PATCH] expected/expected: operator=(#1066) --- reference/expected/expected.md | 2 +- reference/expected/expected/op_assign.md | 245 +++++++++++++++++++++++ 2 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 reference/expected/expected/op_assign.md diff --git a/reference/expected/expected.md b/reference/expected/expected.md index be26126024..60b59f6af2 100644 --- a/reference/expected/expected.md +++ b/reference/expected/expected.md @@ -38,7 +38,7 @@ namespace std { | 名前 | 説明 | 対応バージョン | |-----------------|----------------|-------| -| [`operator=`](expected/op_assign.md.nolink) | 代入演算子 | C++23 | +| [`operator=`](expected/op_assign.md) | 代入演算子 | C++23 | | [`emplace`](expected/emplace.md.nolink) | 正常値型のコンストラクタ引数から直接構築する | C++23 | | [`swap`](expected/swap.md.nolink) | 他の`expected`オブジェクトとデータを入れ替える | C++23 | diff --git a/reference/expected/expected/op_assign.md b/reference/expected/expected/op_assign.md new file mode 100644 index 0000000000..103e901f9e --- /dev/null +++ b/reference/expected/expected/op_assign.md @@ -0,0 +1,245 @@ +# operator= +* expected[meta header] +* function[meta id-type] +* std[meta namespace] +* expected[meta class] +* cpp23[meta cpp] + +```cpp +constexpr expected& operator=(const expected&); // (1) + +constexpr expected& operator=(expected&&) noexcept(see below); // (2) + +template +constexpr expected& operator=(U&&); // (3) + +template +constexpr expected& operator=(const unexpected&); // (4) + +template +constexpr expected& operator=(unexpected&&); // (5) +``` +* see below[italic] +* unexpected[link ../unexpected.md] + +## 概要 +- (1) : コピー代入。 +- (2) : ムーブ代入。 +- (3) : 要素型`T`に変換可能な値を、正常値としてコピー代入またはムーブ代入。 +- (4) : 変換可能な[`unexpected`](../unexpected.md)オブジェクトから、エラー値としてコピー代入。 +- (5) : 変換可能な[`unexpected`](../unexpected.md)オブジェクトから、エラー値としてムーブ代入。 + + +動作説明用の`expected`クラスメンバ変数として、下記を導入する。 + +- `val` : `T`型の正常値。 +- `unex` : `E`型のエラー値。 +- `has_val` : `bool`型のフラグ変数。正常値を保持する場合は`true`に、エラー値を保持する場合は`false`となる。 + +また、説明用のテンプレート関数`reinit-expected`を次の通り定義する。 + +```cpp +template +constexpr void reinit-expected(T& newval, U& oldval, Args&&... args) { + if constexpr (is_nothrow_constructible_v) { + destroy_at(addressof(oldval)); + construct_at(addressof(newval), std::forward(args)...); + } else if constexpr (is_nothrow_move_constructible_v) { + T tmp(std::forward(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)...); + } catch (...) { + construct_at(addressof(oldval), std::move(tmp)); + throw; + } + } +} +``` +* addressof[link /reference/memory/addressof.md] +* construct_at[link /reference/memory/construct_at.md] +* destroy_at[link /reference/memory/destroy_at.md] +* is_nothrow_constructible_v[link /reference/type_traits/is_nothrow_constructible.md] +* is_nothrow_move_constructible_v[link /reference/type_traits/is_nothrow_move_constructible.md] + + +## テンプレートパラメータ制約 +- (2) : 下記を全て満たすこと + - [`is_move_constructible_v`](/reference/type_traits/is_move_constructible.md)` == true` + - [`is_move_assignable_v`](/reference/type_traits/is_move_assignable.md)` == true` + - [`is_move_constructible_v`](/reference/type_traits/is_move_constructible.md)` == true` + - [`is_move_assignable_v`](/reference/type_traits/is_move_assignable.md)` == true` + - `(`[`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)`) == true` +- (3) : 下記を全て満たすこと + - [`is_same_v`](/reference/type_traits/is_same.md)`> == false` + - [`remove_cvref_t`](/reference/type_traits/remove_cvref.md)``が[`unexpected`](../unexpected.md)の特殊化ではない + - [`is_constructible_v`](/reference/type_traits/is_constructible.md)` == true` + - [`is_assignable_v`](/reference/type_traits/is_assignable.md)` == true` + - `(`[`is_nothrow_constructible_v`](/reference/type_traits/is_nothrow_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)`) == true` +- (4) : 下記を全て満たすこと + - [`is_constructible_v`](/reference/type_traits/is_constructible.md)` == true` + - [`is_assignable_v`](/reference/type_traits/is_assignable.md)` == true` + - `(`[`is_nothrow_constructible_v`](/reference/type_traits/is_nothrow_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)` || +`[`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)`) == true` +- (5) : 下記を全て満たすこと + - [`is_constructible_v`](/reference/type_traits/is_constructible.md)` == true` + - [`is_assignable_v`](/reference/type_traits/is_assignable.md)` == true` + - `(`[`is_nothrow_constructible_v`](/reference/type_traits/is_nothrow_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)`) == true` + + +## 効果 +- (1) : 次の処理を行ったのち、例外が送出されなければ、`has_val = rhs.`[`has_value()`](has_value.md.nolink)`; return *this;` + - `this`と`rhs`が共に正常値を保持していたら、`val = *rhs` + - `this`が正常値を保持し、`rhs`がエラー値を保持していたら、`reinit-expected(unex, val, rhs.`[`error()`](error.md.nolink)`)` + - `this`がエラーを保持し、`rhs`が正常値を保持していたら、`reinit-expected(val, unex, *rhs)` + - `this`と`rhs`が共にエラー値を保持していたら、`unex = rhs.`[`error()`](error.md.nolink) +- (2) : 次の処理を行ったのち、例外が送出されなければ、`has_val = rhs.`[`has_value()`](has_value.md.nolink)`; return *this;` + - `this`と`rhs`が共に正常値を保持していたら、`val = std::move(*rhs)` + - `this`が正常値を保持し、`rhs`がエラー値を保持していたら、`reinit-expected(unex, val, std::move(rhs.`[`error()`](error.md.nolink)`))` + - `this`がエラーを保持し、`rhs`が正常値を保持していたら、`reinit-expected(val, unex, std::move(*rhs))` + - `this`と`rhs`が共にエラー値を保持していたら、`unex = std::move(rhs.`[`error()`](error.md.nolink)`)` +- (3) : 次の処理と等価 + - `this`が正常値を保持していたら、`val = std::forward(v)` + - `this`がエラー値を保持していたら、`reinit-expected(val, unex, std::forward(v)); has_val = true;` +- (4) : 次の処理と等価 + - `this`が正常値を保持していたら、`reinit-expected(unex, val, std::forward(e.`[`error()`](error.md.nolink)`)); +has_val = false;` + - `this`がエラー値を保持していたら、`unex = std::forward(e.`[`error()`](error.md.nolink)`);` +- (5) : 次の処理と等価 + - `this`が正常値を保持していたら、`reinit-expected(unex, val, std::forward(e.`[`error()`](error.md.nolink)`)); +has_val = false;` + - `this`がエラー値を保持していたら、`unex = std::forward(e.`[`error()`](error.md.nolink)`);` + + +## 戻り値 +`*this` + + +## 例外 +- (2) : ムーブ代入演算子のnoexcept例外指定は、次の式に従う + - [`is_nothrow_move_assignable_v`](/reference/type_traits/is_nothrow_move_assignable.md)` &&` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)` &&` [`is_nothrow_move_assignable_v`](/reference/type_traits/is_nothrow_move_assignable.md)` &&` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)`` + + +## delete定義される条件 +- (1) : 下記いずれか1つでも満たされないとき、コピー代入演算子はdelete定義される。 + - [`is_copy_assignable_v`](/reference/type_traits/is_copy_assignable.md)` == true` + - [`is_copy_constructible_v`](/reference/type_traits/is_copy_constructible.md)` == true` + - [`is_copy_assignable_v`](/reference/type_traits/is_copy_assignable.md)` == true` + - [`is_copy_constructible_v`](/reference/type_traits/is_copy_constructible.md)` == true` + - `(`[`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)` ||` [`is_nothrow_move_constructible_v`](/reference/type_traits/is_nothrow_move_constructible.md)`) == true` + + +## 例 +```cpp example +#include +#include +#include +#include +#include +#include + +// std::pair型から2要素std::tuple型へはコピー代入可能 +using IntPair = std::pair; +using IntTuple = std::tuple; + +// std::unique_ptr型からstd::shared_ptr型へはムーブ代入可能 +using UniquePtr = std::unique_ptr; +using SharedPtr = std::shared_ptr; + +int main() +{ + // (1) コピー代入 + { + std::expected srcV = 42; + std::expected dstV; + dstV = srcV; + assert(srcV.has_value() && dstV.has_value()); + assert(srcV.value() == 42 && dstV.value() == 42); + + std::expected srcE = std::unexpected{"Oops"}; + std::expected dstE; + dstE = srcE; + assert(!srcE.has_value() && !dstE.has_value()); + assert(srcE.error() == "Oops" && dstE.error() == "Oops"); + } + + // (2) ムーブ代入 + { + std::expected srcV = "ok"; + std::expected dstV; + dstV = std::move(srcV); + assert(srcV.has_value() && dstV.has_value()); + assert(dstV.value() == "ok"); + // srcV.value()はstd::stringムーブ後の未規定の値 + + std::expected srcE = std::unexpected{"ng"}; + std::expected dstE; + dstE = std::move(srcE); + assert(!srcE.has_value() && !dstE.has_value()); + assert(dstE.error() == "ng"); + // srcE.error()はstd::stringムーブ後の未規定の値 + } + + // (3) 正常値の変換コピー代入 + { + IntPair src = IntPair{1, 2}; + std::expected dst; + dst = src; + assert(dst.has_value()); + assert((dst.value() == IntTuple{1, 2})); + } + // (3) 正常値の変換ムーブ代入 + { + UniquePtr src = std::make_unique(42); + std::expected dst; + dst = std::move(src); + assert(dst.has_value()); + assert(*dst.value() == 42); + } + + // (4) エラー値の変換コピー代入 + { + std::unexpected src{IntPair{1, 2}}; + std::expected dst; + dst = src; + assert(not dst.has_value()); + assert((dst.error() == IntTuple{1, 2})); + } + + // (5) エラー値の変換ムーブ代入 + { + std::unexpected src{std::make_unique(42)}; + std::expected dst; + dst = std::move(src); + assert(not dst.has_value()); + assert(*dst.error() == 42); + } +} +``` +* has_value[link has_value.md.nolink] +* value[link value.md.nolink] +* error[link error.md.nolink] +* std::unexpected[link ../unexpected.md] + +### 出力 +``` +``` + + +## バージョン +### 言語 +- C++23 + +### 処理系 +- [Clang](/implementation.md#clang): 16.0 +- [GCC](/implementation.md#gcc): 12.1 +- [ICC](/implementation.md#icc): ?? +- [Visual C++](/implementation.md#visual_cpp): ?? + + +## 参照 +- [P0323R12 std::expected](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0323r12.html)