1. ホーム
  2. c++

[解決済み】 正のラムダ:'+[]{}' - これは何の妖術か?[重複している]

2022-03-30 15:06:50

質問

Stack Overflowの質問で C++11ではラムダの再定義ができない、なぜ? という、コンパイルできない小さなプログラムが渡されました。

int main() {
    auto test = []{};
    test = []{};
}

質問にも答えていただき、すべて順調のようでした。その後 ヨハネス・シャウプ を作成し 興味深い見解です。 :

をつけると + の前に置くと、魔法のように動き出します。

そこで気になるのは、なぜ次のようにするとうまくいくのか、ということです。

int main() {
    auto test = +[]{}; // Note the unary operator + before the lambda
    test = []{};
}

の両方でも問題なくコンパイルできます。 GCC 4.7+と Clang 3.2+. コードの規格は適合していますか?

解決方法は?

はい、コードは標準に準拠しています。その + は、ラムダを単なる古い関数ポインタに変換するきっかけとなります。

どうなるかというと、こうなります。

コンパイラは、最初のラムダ( []{} ) に従って、クロージャ・オブジェクトを生成します。このラムダは 非捕獲 lambdaを使用すると、次のようになります。

5.1.2 ラムダ式 [expr.prim.lambda] の場合

6 のクロージャーのタイプは ラムダ式 がなく ラムダキャプチャ は,クロージャ型の関数呼び出し演算子と同じパラメータと戻り値の型を持つ関数へのポインタへの,パブリックな非仮想的な非明示的 const 変換関数を持っている。この変換関数が返す値は、呼び出されたときにクロージャ型の関数呼び出し演算子を呼び出すのと同じ効果を持つ関数のアドレスでなければならない。

これは重要なことで、単項演算子 + には組み込みのオーバーロードのセットがあり、特にこのオーバーロードは重要です。

13.6 組み込み演算子[over.built]について

8 各タイプについて T という形の演算子関数の候補が存在する。

T* operator+(T*);

そして、これによって、何が起こるかは非常に明確です。演算子 + がクロージャオブジェクトに適用されると、オーバーロードされた組み込みの候補のセットは、変換からANYポインタを含み、クロージャタイプは、ラムダの関数ポインタへの変換というちょうど1つの候補を含んでいます。

の型は testauto test = +[]{}; は次のように推論されます。 void(*)() . 2行目は簡単です。2番目のラムダ/クロージャオブジェクトに対して、関数ポインタへの代入が1行目と同じ変換を引き起こします。たとえ2番目のラムダが異なるクロージャの型を持っていても、結果の関数ポインタはもちろん互換性があり、代入することができます。