@@ -45,23 +45,23 @@ auto example4(Jeff x) -> int& {
4545}
4646```
4747
48- 上記例の`example3(), example4()`は、C++20までは戻り値型が右辺値参照型であるために `return`文でコピーは発生しないため、暗黙ムーブの対象になっていなかった。C++23では、これらの場合にも暗黙ムーブが試みられるようになる。
48+ 上記例の`example3(), example4()`は、C++20までは戻り値型が参照型であるために `return`文でコピーは発生しないため、暗黙ムーブの対象になっていなかった。C++23では、これらの場合にも暗黙ムーブが試みられるようになる。
4949
5050また同時に、C++11で導入されて以降少しづつその対象(暗黙ムーブが起こる場合および起こる場所)を拡大してきたことで複雑化していた暗黙ムーブ仕様が整理され、その仕様と実装がかなり単純化された。
5151
5252## 仕様
5353
54- 暗黙ムーブ可能なエンティティ(*implicitly movable entity*)を次のように指定する
54+ ** 暗黙ムーブ可能なエンティティ** (*implicitly movable entity*)を次のどちらかとして指定する
5555
5656- 自動記憶域期間の非`volatile`オブジェクト型変数
5757- 自動記憶域期間の非`volatile`オブジェクト型への右辺値参照
5858
59- 暗黙ムーブ可能なエンティティが次の場所で指名されている場合、その式はムーブする資格がある (*move-eligible*な)式となる
59+ 暗黙ムーブ可能なエンティティが次の場所で指名されている場合、その式は**ムーブする資格がある式** (*move-eligible*)となる
6060
61- - `return/co_return`文のオペランド
61+ - `return/co_return`文のオペランド(以下の条件を全て満たすもの)
6262 - オペランドは変数名を指定する式(*id-expression*)であり(`()`で囲まれていても良い)
6363 - その変数名は、その文を囲む最も内側の関数(もしくはラムダ式)の本体内もしくは関数引数宣言内の、暗黙ムーブ可能なエンティティを指定している
64- - `throw`式のオペランド
64+ - `throw`式のオペランド(以下の条件を全て満たすもの)
6565 - オペランドは変数名を指定する式であり(`()`で囲まれていても良い)
6666 - その変数名のスコープは、囲む最も内側のtryブロックのスコープよりも長くなく
6767 - その変数名は暗黙ムーブ可能なエンティティを指定している
@@ -73,7 +73,92 @@ auto example4(Jeff x) -> int& {
7373ムーブする資格がある式は必ず変数名を指定する式(*id-expression*)であるため、*prvalue*な`return`文オペランドに対して適用される[コピー省略](/lang/cpp17/guaranteed_copy_elision.md)と複合することは無い。NRVOとは複合しうるが、その場合は暗黙ムーブによって呼び出されるムーブコンストラクタの呼び出しがNRVOによって省略される。
7474
7575### 副作用
76- (執筆中)
76+
77+ この仕様の単純化はいくつか以前の動作を変更している。
78+
79+ まず、`return`文のオペランドがムーブする資格がある式となる場合その式の値カテゴリは*xvalue*として扱われることで、戻り値型推論の結果が変わる場合がある。
80+
81+ ```cpp
82+ auto f(int n) -> decltype(auto) {
83+ return (n); // かっこに囲まれたid-expression、ムーブする資格がある式
84+ }
85+ // C++20 : 戻り値型はint&
86+ // C++23 : 戻り値型はint&&
87+
88+ auto g(int n) -> auto&& {
89+ return n; // id-expression、ムーブする資格がある式
90+ }
91+ // C++20 : 戻り値型はint&
92+ // C++23 : 戻り値型はint&&
93+ ```
94+
95+ ` decltype(auto) ` による戻り値型推論では、` return ` 文のオペランドを` decltype ` することで戻り値型を推論する。` decltype ` は変数名に対して使用された時はその変数の宣言された型を取得するが、変数名がかっこに囲まれている場合はかっこに囲まれた* id-expression* として値カテゴリを含めたその式の型を取得する。このため、上記` f() ` のように、` decltype(auto) ` 戻り値型の関数で` return ` 文のオペランドがかっこに囲まれた変数名であり、C++23でそのオペランドがムーブする資格がある式となる場合、その値カテゴリが変更される(* lvalue* -> * xvalue* )ことによって戻り値型推論結果が変化する。
96+
97+ ` auto&& ` による戻り値型推論は` decltype ` と推論方法が異なるものの、やはり` return ` 文のオペランドの値カテゴリに応じて参照修飾が決定されるため、従来左辺値(* lvalue* )だったオペランドがC++23でムーブする資格がある式となる場合に、値カテゴリが* xvalue* となることによって推論結果が変化する。
98+
99+ この他の場合には結果は変化しない。
100+
101+ ``` cpp
102+ auto f1 (int n)-> decltype(auto) {
103+ return n;
104+ }
105+ // C++20/23共に戻り値型はint
106+
107+ auto f2(int n)-> auto {
108+ return n;
109+ }
110+ // C++20/23共に戻り値型はint
111+
112+ auto f3(int n)-> auto& {
113+ return n;
114+ }
115+ // C++20/23共に戻り値型はint&
116+ // ただし、後述のようにC++23ではエラー
117+
118+ auto f4(int n)-> const auto& {
119+ return n;
120+ }
121+ // C++20/23共に戻り値型はconst int&
122+ ```
123+
124+ そして、同様に`return`文オペランドの値カテゴリが変化することによって、ローカル変数の参照を返す一部の関数が不適格になる様になる。
125+
126+ ```cpp
127+ auto f() -> int& {
128+ int n = 10;
129+
130+ return n; // ng、nはムーブする資格のある式であり、その型と値カテゴリはint&&(戻り値型と一致しない)
131+ }
132+
133+ auto g() -> std::reference_wrapper<int> {
134+ int n = 10;
135+
136+ return n; // ng、nはムーブする資格のある式であり、その型と値カテゴリはint&&(reference_wrapperのコンストラクタで拒否される)
137+ }
138+ ```
139+ * reference_wrapper[ link /reference/functional/reference_wrapper.md]
140+
141+ ただし、ローカル参照変数は暗黙ムーブ可能なエンティティではないため、ローカル参照を返そうとする場合従来通りエラーにはならない。
142+
143+ ``` cpp
144+ auto f () -> int& {
145+ int n = 10;
146+ int& r = n;
147+
148+ return r; // ok、rはムーブする資格のある式ではなく、その型と値カテゴリはint&
149+ }
150+ ```
151+
152+ また、この変更とは逆に、ローカル変数の右辺値参照を返そうとする場合が適格になってしまう。
153+
154+ ``` cpp
155+ auto f () -> int&& {
156+ int n = 10;
157+
158+ return n; // C++20ではng
159+ // C++23ではok、nはムーブする資格のある式であり、その型と値カテゴリはint&&
160+ }
161+ ```
77162
78163## 例
79164(執筆中)
0 commit comments