Skip to content

Commit 8384e0b

Browse files
committed
暗黙ムーブ : 経緯を追加 #1021
1 parent 4a58c6b commit 8384e0b

File tree

1 file changed

+53
-6
lines changed

1 file changed

+53
-6
lines changed

lang/cpp23/simpler_implicit_move.md

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,20 @@ auto g(bool b) -> Weird {
207207
}
208208
```
209209
210+
```cpp
211+
// ムーブしないことを明示する関数、C++20までは使用可能
212+
template<typename T>
213+
auto unmove(T&& v) -> T& {
214+
return v; // C++23からng、vはxvalue
215+
}
216+
217+
// C++23では次のように修正する必要がある
218+
template<typename T>
219+
auto unmove(T&& v) -> T& {
220+
return static_cast<T&>(v); // ok、キャスト式はムーブする資格のある式ではない
221+
}
222+
```
223+
210224
```cpp
211225
// 例示用のムーブ可能な型
212226
struct Widget {
@@ -284,15 +298,48 @@ auto f10(J x) -> Widget& {
284298
```
285299
286300
## この機能が必要になった背景・経緯
287-
(執筆中)
288301
289-
## 検討されたほかの選択肢
290-
(執筆中)
302+
C++20までは戻り値型が参照型である場合に暗黙ムーブが行われていなかった。より正確には、関数戻り値の初期化時に戻り値構築のためのコンストラクタを選択するオーバーロード解決が行われる場合にのみ、暗黙ムーブが考慮されていたが、戻り値型が参照型の場合は参照の初期化だけが起こりコンストラクタ呼び出しは関与しないため暗黙ムーブは考慮されていなかった。
303+
304+
これによって、同じ`return`文でも戻り値型が異なることでそのオペランドの扱いが変化してしまっていた。
305+
306+
```cpp
307+
// Widget, RRefTakerは上の例を参照
308+
// どちらも、C++20の場合
309+
310+
auto f(Widget&& w) -> RRefTaker {
311+
return w; // ok、wは右辺値として扱われる
312+
}
313+
314+
auto g(Widget&& w) -> Widget&& {
315+
return w; // ng、wは左辺値として扱われる
316+
}
317+
```
318+
319+
この非一貫性を解消することがまず求められていた。
320+
321+
また、C++20時点の暗黙ムーブの使用は二段階のオーバーロード解決を行う複雑なものであり、暗黙ムーブが行われるかどうかについて実装間で挙動に差異が生じていた。
322+
323+
C++20の仕様は次のようになっていた。
324+
325+
暗黙ムーブ可能なエンティティが次のコピー初期化が行われる場所で指名されている場合、コピーの代わりにムーブが使用される場合がある
326+
327+
- `return/co_return`文のオペランド
328+
- (諸条件はC++23仕様と同一なので省略)
329+
- `throw`式のオペランド
330+
- (諸条件はC++23仕様と同一なので省略)
331+
332+
これらのコピー初期化が行われる場所において、呼び出されるコンストラクタまたは[`return_­value()`オーバーロード](/lang/cpp20/coroutines.md)を選択するオーバーロード解決は次の順序で実行される
333+
334+
1. オペランドのid式を右辺値(*rvalue*)としてオーバーロード解決を実行する
335+
2. 1が失敗した(もしくは行われなかった)場合、オペランドのid式を左辺値(*lvalue*)としてオーバーロード解決を実行する
336+
337+
この最後の手順の1回目のオーバーロード解決時にコピーコンストラクタの代わりにムーブコンストラクタが選択されることによって、C++20の暗黙ムーブは達成されていた。しかし、この手順2における「失敗」という言葉の意味が明確ではなく、それによって実装間で暗黙ムーブが行われるかどうか、あるいは選択されるコンストラクタに差異が生じていた。
291338

292-
## 関連項目
293-
(執筆中)
339+
`return`文オペランドの扱いの戻り値型の違いによる非一貫性と、使用の複雑さと曖昧さによる実装間の差異の2つの問題を解決するために、C++23ではムーブする資格がある式という概念を用いて暗黙ムーブの仕様が単純化された。
294340

295341
## 参照
296342

297343
- [P2266R3 Simpler implicit move](https://wg21.link/p2266r3)
298-
- [The Complete Guide to `return x;` - Arthur O'Dwyer - [CppNow 2021] - YouTube](https://www.youtube.com/watch?v=OGKAJD7bmr8)
344+
- [The Complete Guide to `return x;` - Arthur O'Dwyer - [CppNow 2021] - YouTube](https://www.youtube.com/watch?v=OGKAJD7bmr8)
345+
- [c++ - Does c++23 break unmove - Stack Overflow](https://stackoverflow.com/questions/76647046/does-c23-break-unmove)

0 commit comments

Comments
 (0)