301

スタック オーバーフローの質問では、C++ 11 で許可されていないラムダを再定義するのはなぜですか? 、コンパイルされない小さなプログラムが与えられました:

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

質問は答えられ、すべて問題ないようでした。次にJohannes Schaubが来て、興味深い観察を行いました。

+最初のラムダの前にa を置くと、魔法のように機能し始めます。

だから私は興味があります:なぜ次のように機能するのですか?

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

GCC 4.7+ とClang 3.2+の両方で正常にコンパイルされます。コード標準は準拠していますか?

4

1 に答える 1

278

はい、コードは標準に準拠しています。は+、ラムダの単純な古い関数ポインターへの変換をトリガーします。

何が起こるかは次のとおりです。

コンパイラは最初のラムダ ( []{}) を見て、§5.1.2 に従ってクロージャ オブジェクトを生成します。ラムダは非キャプチャラムダであるため、以下が適用されます。

5.1.2 ラムダ式 [expr.prim.lambda]

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

単項演算子+には組み込みのオーバーロードのセット、具体的には次のオーバーロードがあるため、これは重要です。

13.6 組み込み演算子 [over.built]

8すべてのタイプについてT、次の形式の演算子関数の候補が存在します。

    T* operator+(T*);

これにより、何が起こるかは非常に明確です: operator+がクロージャー オブジェクトに適用されると、オーバーロードされた組み込み候補のセットには任意ポインターへの変換が含まれ、クロージャー型には 1 つの候補が含まれます: 関数への変換ラムダのポインター。

testしたがって、 inの型はauto test = +[]{};と推定されvoid(*)()ます。2 行目は簡単です。2 番目のラムダ/クロージャー オブジェクトでは、関数ポインターへの代入によって、1 行目と同じ変換がトリガーされます。2 番目のラムダのクロージャー型は異なりますが、結果として得られる関数ポインターはもちろん互換性があり、代入できます。

于 2013-09-19T07:51:31.910 に答える