From 51022b16e885b1294391276a21c8577006c08e9d Mon Sep 17 00:00:00 2001 From: yumetodo Date: Wed, 11 Jan 2023 00:44:41 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20P0588R1=E3=81=A7=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9Fodr-usable=E3=81=AB=E3=81=A4?= =?UTF-8?q?=E3=81=84=E3=81=A6=E8=A8=98=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref: - https://timsong-cpp.github.io/cppwp/n4861/basic.def.odr#9 - https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html --- .../simplifying_implicit_lambda_capture.md | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lang/cpp20/simplifying_implicit_lambda_capture.md b/lang/cpp20/simplifying_implicit_lambda_capture.md index 8fbce21ed2..1f4ab34e9d 100644 --- a/lang/cpp20/simplifying_implicit_lambda_capture.md +++ b/lang/cpp20/simplifying_implicit_lambda_capture.md @@ -29,12 +29,31 @@ void f() { } ``` -構造化束縛で導入された名前はラムダ式でキャプチャできない、と明記された。しかしその後、「[構造化束縛を拡張して通常の変数宣言のように使用できるようにする](extending_structured_bindings_to_be_more_like_variable_declarations.md)」の仕様でそれが可能となったため、この仕様変更は打ち消された。 +本提案では一旦、構造化束縛で導入された名前はラムダ式でキャプチャできない、と明記された。その後、「[構造化束縛を拡張して通常の変数宣言のように使用できるようにする](extending_structured_bindings_to_be_more_like_variable_declarations.md)」の仕様でそれが可能となったため、この部分は打ち消された。 +## 仕様 + +odr-usable(=ある名前の定義がその場所で見つかるか)という用語を導入して、lamda式でキャプチャできるものの増加に対して、規格文面の整理が行われた。 + +ローカル変数のように、ローカルでodr-usedされる時に、その場所ではodr-usableでないとき、そのプログラムは適格ではない。 + +```cpp +void f(int n) { + [] { n = 1; }; // error: nが使われようとしているのはlambda式の中なので、odr-usableではない + struct A { + void f() { n = 2; } // error: nが使われようとしているのは関数定義スコープの中なので、odr-usableではない + }; + void g(int = n); // error: nが使われ用としているのはlambda式以外の関数引数スコープの中なので、odr-usableではない + [=](int k = n) {}; // error: n is not odr-usable due to being + // outside the block scope of the lambda-expression + [&] { [n]{ return n; }; }; // OK +} +``` ## 関連項目 - [C++17 構造化束縛](/lang/cpp17/structured_bindings.md) - [C++20 構造化束縛を拡張して通常の変数宣言のように使用できるようにする](extending_structured_bindings_to_be_more_like_variable_declarations.md) +- [C++20 構造化束縛した変数の参照キャプチャを許可](reference_capture_of_structured_bindings.md) ## 参照 - [P0588R1 Simplifying implicit lambda capture](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html) From 68017b39bf498e96d0cfee4471d7b232f76851a4 Mon Sep 17 00:00:00 2001 From: yumetodo Date: Wed, 11 Jan 2023 01:07:27 +0900 Subject: [PATCH 2/3] chore: fix typo --- lang/cpp20/simplifying_implicit_lambda_capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/cpp20/simplifying_implicit_lambda_capture.md b/lang/cpp20/simplifying_implicit_lambda_capture.md index 1f4ab34e9d..6d93d39cc3 100644 --- a/lang/cpp20/simplifying_implicit_lambda_capture.md +++ b/lang/cpp20/simplifying_implicit_lambda_capture.md @@ -43,7 +43,7 @@ void f(int n) { struct A { void f() { n = 2; } // error: nが使われようとしているのは関数定義スコープの中なので、odr-usableではない }; - void g(int = n); // error: nが使われ用としているのはlambda式以外の関数引数スコープの中なので、odr-usableではない + void g(int = n); // error: nが使われようとしているのはlambda式以外の関数引数スコープの中なので、odr-usableではない [=](int k = n) {}; // error: n is not odr-usable due to being // outside the block scope of the lambda-expression [&] { [n]{ return n; }; }; // OK From 716bb0ecf14a57cf52ad184b906692fa079f6638 Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Tue, 15 Sep 2026 15:54:51 +0900 Subject: [PATCH 3/3] =?UTF-8?q?C++20=20=E6=9A=97=E9=BB=99=E3=81=AE?= =?UTF-8?q?=E3=83=A9=E3=83=A0=E3=83=80=E3=82=AD=E3=83=A3=E3=83=97=E3=83=81?= =?UTF-8?q?=E3=83=A3=E3=82=92=E7=B0=A1=E7=95=A5=E5=8C=96=20:=20odr-usable?= =?UTF-8?q?=E3=81=AE=E8=AA=AC=E6=98=8E=E3=82=92=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simplifying_implicit_lambda_capture.md | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/lang/cpp20/simplifying_implicit_lambda_capture.md b/lang/cpp20/simplifying_implicit_lambda_capture.md index 6d93d39cc3..41c7052320 100644 --- a/lang/cpp20/simplifying_implicit_lambda_capture.md +++ b/lang/cpp20/simplifying_implicit_lambda_capture.md @@ -32,24 +32,50 @@ void f() { 本提案では一旦、構造化束縛で導入された名前はラムダ式でキャプチャできない、と明記された。その後、「[構造化束縛を拡張して通常の変数宣言のように使用できるようにする](extending_structured_bindings_to_be_more_like_variable_declarations.md)」の仕様でそれが可能となったため、この部分は打ち消された。 ## 仕様 +ラムダ式がどのローカル変数をキャプチャできるか、またそれをどこで使用できるかを規定するために、odr-usableという用語が導入された。 -odr-usable(=ある名前の定義がその場所で見つかるか)という用語を導入して、lamda式でキャプチャできるものの増加に対して、規格文面の整理が行われた。 +ローカル変数、関数のパラメータ、構造化束縛で導入された名前、`*this`のように、関数のローカルな文脈に属するものをローカルエンティティ (*local entity*) と呼ぶ。ローカルエンティティは、その宣言から使用する地点までの間に挟まるスコープが、すべて以下のいずれかである場合に、その地点でodr-usableとなる。 -ローカル変数のように、ローカルでodr-usedされる時に、その場所ではodr-usableでないとき、そのプログラムは適格ではない。 +- ブロックスコープ (`{}`で囲まれた文のスコープ) +- ラムダ式の関数パラメータスコープ (ラムダ式のパラメータを宣言できる範囲) であり、そのラムダ式がそのローカルエンティティを明示的にキャプチャしているかデフォルトキャプチャをもっていて、かつそのラムダ式の本体のブロックスコープも、使用する地点までの間に挟まっている + +odr-usableではないローカルエンティティをodr-usedした場合、そのプログラムは不適格となる。 ```cpp void f(int n) { - [] { n = 1; }; // error: nが使われようとしているのはlambda式の中なので、odr-usableではない + [] { n = 1; }; // エラー: nをキャプチャしていないため、odr-usableではない + [&] { n = 1; }; // OK: nをキャプチャしており、ラムダ式の本体の中で使用している + struct A { - void f() { n = 2; } // error: nが使われようとしているのは関数定義スコープの中なので、odr-usableではない - }; - void g(int = n); // error: nが使われようとしているのはlambda式以外の関数引数スコープの中なので、odr-usableではない - [=](int k = n) {}; // error: n is not odr-usable due to being - // outside the block scope of the lambda-expression - [&] { [n]{ return n; }; }; // OK + void g() { n = 2; } // エラー: ローカルクラスのスコープと、そのメンバ関数の + }; // 関数パラメータスコープが挟まる + + void g(int = n); // エラー: ラムダ式ではない関数の関数パラメータスコープが挟まる + + [=](int k = n) {}; // エラー: ラムダ式の関数パラメータスコープは挟まるが、 + // 本体のブロックスコープは挟まらない + + [&] { [n]{ return n; }; }; // OK: 内側のラムダ式は、外側のラムダ式の本体の中にある } ``` +### ラムダ式のデフォルト引数で外側のローカル変数を使用できない理由 +キャプチャが効くのはラムダ式の本体の中だけであり、パラメータの宣言はその外側にある。そのため`[=](int k = n) {}`の`n`は、キャプチャしたコピーではなく、外側の`n`そのものを指す。 + +デフォルト引数が評価されるのは、ラムダ式を書いた場所ではなく、クロージャオブジェクトを呼び出した場所である。クロージャオブジェクトはそれを作った関数の外へ持ち出せるため、仮にこれを許すと、すでに生存期間の終わったローカル変数を読むことになりうる。 + +```cpp +auto f(int n) { + return [=](int k = n) { return k; }; // エラー +} + +int main() { + auto g = f(3); + g(); // 仮に適格だとすると、ここで関数f()のパラメータnを読むことになる +} +``` + + ## 関連項目 - [C++17 構造化束縛](/lang/cpp17/structured_bindings.md) - [C++20 構造化束縛を拡張して通常の変数宣言のように使用できるようにする](extending_structured_bindings_to_be_more_like_variable_declarations.md) @@ -57,3 +83,6 @@ void f(int n) { ## 参照 - [P0588R1 Simplifying implicit lambda capture](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html) + - C++20で、ラムダ式での暗黙のキャプチャの規定が整理され、odr-usableが導入された +- [CWG Issue 2380. capture-default makes too many references odr-usable](https://cplusplus.github.io/CWG/issues/2380.html) + - C++20の策定中に、odr-usableの条件へ「ラムダ式の本体のブロックスコープも挟まっていること」が追加された。この条件がないと、デフォルトキャプチャを書くだけでラムダ式のパラメータのデフォルト引数から外側のローカル変数を使用できてしまっていた