const変数をラムダで捕捉する必要がない場合があるのはなぜか?
質問
次のような例を考えてみましょう。
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
なぜ
n
を捕捉する必要があるのに、2番目のラムダでは
m
はないのですか?5.1.2項を確認したところ、(
ラムダ式
) を調べましたが、理由を見つけることができませんでした。このことが説明されている段落を教えていただけませんか。
更新: 私はこの動作を GCC 6.3.1 と 7 (trunk) の両方で観察しました。Clang 4.0 と 5 (trunk) では、両方のケースでエラーで失敗しました (
variable 'm' cannot be implicitly captured in a lambda with no capture-default specified
).
どのように解決するのですか?
ブロックスコープでのラムダでは、ブロックスコープ内で特定の条件を満たす変数が スコープに到達する にある特定の条件を満たす変数は、たとえそれがキャプチャされていなくても、ラムダ内部で限定的に使用することができる。
大雑把に言うと
スコープに到達
には、ラムダを含む関数のローカル変数で、ラムダが定義された時点でスコープ内にあるものが含まれます。 つまり、これには
m
と
n
を追加しました。
"ある基準"と"限定された方法"は、具体的には(C++14時点)です。
-
ラムダ内部で、変数が
odr-usedです。
以外の操作を受けてはならないことを意味します。
-
が捨て値表現として現れること (
m;
はその一つ)、または - その値が取得されること。
-
が捨て値表現として現れること (
-
変数はどちらかでなければなりません。
-
A
const
非volatile
整数またはenumでイニシャライザーが 定数式 または -
A
constexpr
非volatile
変数(またはそのサブオブジェクト)
-
A
C++14 への参照: [expr.const]/2.7, [basic.def.odr]/3 (最初の文), [expr.prim.lambda]/12, [expr.prim.lambda]/10.。
これらのルールの根拠は、他のコメント/回答で示唆されているように、コンパイラはブロックから独立した自由関数として捕捉なしラムダを "synthesize" できる必要があるからです (そのようなものは関数へのポインタに変換されることができるので)。それは変数が常に同じ値を持っているだろうことを知っていれば、変数を参照してもこれを実行できますし、コンテキストから独立して変数値を得るための手順を繰り返すこともできます。 しかし、変数が時々刻々と変化する可能性がある場合や、変数のアドレスが必要な場合などには、このようなことは行えません。
あなたのコードでは
n
は定数でない式で初期化されていました。 したがって
n
は捕捉されることなくラムダで使用することはできません。
m
は定数式で初期化された
42
で初期化されているので、quot;certain criteria"を満たしています。捨て値式は式をodr-useしないので
m;
がなくても
m
は捕捉されません。
この2つのコンパイラの違いは、clangが考慮するのは
m;
をodr-useにすることです。
m
を使いますが、gccは使いません。 basic.def.odr]/3 の最初の文は、かなり複雑です。
変数
x
で、その名前が評価される可能性のある式として表示されます。ex
は odr-usedです。 によってex
にlvalueからrvalueへの変換を適用しなければx
が自明でない関数を呼び出さない定数式を生成し、もしx
がオブジェクトであればex
は式の結果の候補の集合の要素です。e
に lvalue から rvalue への変換が適用される場合です。e
に適用されるか、あるいはe
は捨て値の式です。
が、よく読むと、discarded-value expressionは、以下のようなことはしないと明記されています。 odr-use を使用しないことが明記されています。
C++11 の [basic.def.odr] バージョンにはもともと廃棄値式のケースが含まれていないため、公開されている C++11 では clang の動作は正しいでしょう。 しかし、C++14 で表示されているテキストは、C++11 に対する欠陥として受け入れられました ( 課題712 ) のため、コンパイラーは C++11 モードであってもその動作を更新する必要があります。
関連
-
[解決済み】構造体のベクター初期化について
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み] Export Default Const」が無効なのはなぜですか?
-
[解決済み] PythonにMultiline Lambdaはない。なぜダメなのか?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++でint型に無限大を設定する
-
[解決済み】C++のGetlineの問題(オーバーロードされた関数 "getline "のインスタンスがない
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】変数 '' を抽象型 '' と宣言できない。
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】エラー:free(): 次のサイズが無効です(fast)。
-
[解決済み] 解決済み] `pthread_create' への未定義の参照 [重複] [重複
-
[解決済み】デバッグアサーションに失敗しました