6

C ++ 11ラムダで参照によってオブジェクトをキャプチャし、オブジェクトをスコープ外に出してからラムダを実行すると、オブジェクトにアクセスできます。次のコードを実行すると、デストラクタがすでに呼び出されていても、ラムダ呼び出しはオブジェクトにアクセスできます。なぜこれが機能するのか、なぜランタイムエラーが発生しないのかを誰かが説明できますか?

#include <iostream>

class MyClass {
public:
    int health = 5;
    MyClass() {std::cout << "MyClass created!\n";}
    ~MyClass() {std::cout << "MyClass destroyed!\n";}
};

int main(int argc, const char * argv[])
{
    std::function<bool (int)> checkHealth;
    if(true) {
        MyClass myVanishingObject;
        checkHealth = [&myVanishingObject] (int minimumHealth) -> bool {
            std::cout << myVanishingObject.health << std::endl;
            return myVanishingObject.health >= minimumHealth;
        };
    } // myVanishingObject goes out of scope

    // let's do something with the callback to test if myVanishingObject still exists.
    if(checkHealth(4)) {
        std::cout << "has enough health\n";
    } else {
        std::cout << "doesn't have enough health\n";
    }
    return 0;
}

出力は次のとおりです。

MyClass created!
MyClass destroyed!
5
has enough health
4

1 に答える 1

18

cppreference.com Webサイトのラムダ関数のドキュメントによると

ダングリング参照

エンティティが暗黙的または明示的に参照によってキャプチャされ、エンティティの有効期間が終了した後にクロージャー オブジェクトの関数呼び出し演算子が呼び出されると、未定義の動作が発生します。C++ クロージャは、キャプチャされた参照の有効期間を延長しません。

つまり、参照によってオブジェクトをキャプチャし、オブジェクトの有効期間を終了させたという事実は、ラムダを呼び出すと未定義の動作が発生することを意味します。UB が機能する可能性のある 1 つの方法は、「オブジェクトが死んでいるにもかかわらず、オブジェクトが生きていて正常に見える」ことであるため、何も問題がないように見える未定義の動作を目にしているのではないかと思います。

コンパイラが一意のスタックの場所を一時変数に割り当てた場合、これが当てはまると思います。これは、オブジェクトの有効期間が終了した後、main戻る前に、メモリが何にも触れられないことを意味します。5したがって、他に何も書き込まれていないため、変数が以前と同じように値を保持していることがわかります。

お役に立てれば!

于 2013-02-08T00:11:57.563 に答える