C++11のラムダで参照を参照で捕捉する
質問
これを考えてみましょう。
#include <functional>
#include <iostream>
std::function<void()> make_function(int& x) {
return [&]{ std::cout << x << std::endl; };
}
int main() {
int i = 3;
auto f = make_function(i);
i = 5;
f();
}
このプログラムは
5
を出力することが保証されていますか?
をキャプチャすると、どのように動作するかは理解しています。
x
を値で捕捉することができます (
[=]
を参照で捕捉することによって、未定義の動作を呼び出しているのかどうか、よくわかりません。もしかしたら、この後
make_function
が戻った後、ぶら下がった参照を持つことになるでしょうか。それとも、元々参照されていたオブジェクトがまだそこにある限り、キャプチャされた参照は動作することが保証されているのでしょうか?
ここで決定的な標準ベースの答えを探しています :) 実際には十分に機能する 今のところ ;)
どのように解決するのですか?
コードの動作は保証されています。
標準の文言を掘り下げる前に:このコードが動作することは、C++ 委員会の意図するところです。しかし、現状の表現はこれに関して十分に明確でないと考えられていたため (実際、C++14 以降の標準に加えられたバグ修正により、このコードを動作させるための繊細な取り決めが壊されました)、以下のようになりました。 CWG 問題 2011 は問題を明確にするために提起され、現在委員会を通して進行中です。私が知る限りでは、これを間違っている実装はありません。
Ben Voigt の回答には事実誤認が含まれており、混乱を招いているため、いくつかの点を明らかにしたいと思います。
- スコープとは、C++ における静的で語彙的な概念であり、プログラム ソース コードの領域を記述し、非限定名前検索によって特定の名前を宣言と関連付けます。ライフタイムとは関係ありません。参照 [基本.スコープ.宣言的]/1 .
-
ラムダの "reaching scope" 規則は、同様に、キャプチャがいつ許可されるかを決定する構文特性である。例えば
void f(int n) { struct A { void g() { // reaching scope of lambda starts here [&] { int k = n; }; // ...
n
はここでスコープに入っていますが、ラムダの到達スコープには含まれていないので、捕捉することはできません。別の言い方をすれば、ラムダの到達スコープとは、どこまで到達して変数を捕捉できるかを示すもので、(ラムダ以外の)関数とそのパラメータまでは到達できるが、その外に到達して外に現れる宣言を捕捉できるわけではない。
ですから、スコープに到達するという概念は、この質問には関係ありません。キャプチャされるエンティティは
make_function
のパラメータ
x
で、これはラムダの到達範囲内にあります。
OK、ではこの問題についての標準の表現を見てみましょう。expr.prim.lambda]/17によると、唯一の id-expression のみがラムダクロージャ型のメンバーアクセスに変換されます。 id-式 はそのままにされ、包含するスコープで示されるのと同じ実体を示すようになります。
これはすぐに悪いことに思えます。
x
の寿命は終わっているので、どうやってそれを参照するのでしょうか?その場合、その参照はスコープ内にあり、おそらく使用することができます。または、それはクラスのメンバーであり、メンバーアクセス式が有効であるためにクラス自体がその寿命内になければなりません。その結果、標準ではごく最近まで、寿命外の参照の使用は禁止されていませんでした。
ラムダの表現は、寿命の外で参照を使用してもペナルティがないという事実を利用し、参照によって捕捉された実体へのアクセスが何を意味するかについて、いかなる明示的なルールも与える必要がありませんでした - それはただ、その実体を使うことを意味します。そして、これはごく最近まで (C++11 と C++14 を含む) 動作が保証されていた方法です。
しかしながら、それは かなり 特に、それ自身のイニシャライザ内、参照より前のクラスメンバのイニシャライザ、またはそれが名前空間スコープ変数で、それ以前に初期化された別のグローバルからアクセスする場合は、参照できます。 CWG2012年問題 はこの見落としを修正するために導入されましたが、不注意にも、参照の参照によるラムダ捕捉の仕様を壊してしまいました。C++17 が出荷される前にこのリグレッションを修正すべきです。それが適切に優先されることを確認するために、私は National Body (国内団体) コメントを提出しました。
関連
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】C-stringを使用すると警告が表示される。"ローカル変数に関連するスタックメモリのアドレスが返される"
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み] リスト内包とラムダ+フィルタの比較
-
[解決済み] クロージャ」と「ラムダ」の違いは何ですか?
-
[解決済み] Distinct() with lambda?
-
[解決済み] ラムダ(関数)とは何ですか?
-
[解決済み】C++11のラムダ式って何?
-
[解決済み】キャプチャしたラムダを関数ポインタとして渡す
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 unsigned int vs. size_t
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】ファイルから整数を読み込んで配列に格納する C++ 【クローズド
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み] 解決済み] `pthread_create' への未定義の参照 [重複] [重複
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++