参照先が生きていてもポインタ変数自体がラムダ式より先に寿命が来る場合はそのポインタ変数を参照キャプチャしない方がいい【C++】

試した環境

Microsoft Visual Studio Community 2019 Version 16.4.5

本題

最近私がやらかしたミスを書き残します。

#include <functional>
#include <iostream>

class MyCallee {
public:
    MyCallee() : counter_(0) {}
    int countUp() {
        return ++counter_;
    }
private:
    int counter_;
};

class MyCaller {
public:
    MyCaller(MyCallee* p) {
        func1_ = [=]() { std::cout << p->countUp() << std::endl; }; // OK
        func2_ = [&]() { std::cout << p->countUp() << std::endl; }; // NG
    }

    void call1() { func1_(); }
    void call2() { func2_(); }
private:

    std::function<void(void)> func1_;
    std::function<void(void)> func2_;
};

int main()
{
    MyCallee callee;
    MyCaller caller(&callee);
    caller.call1();
    caller.call2();
}

上記のコードですが、caller.call2();を呼ぶとアクセスエラーが起こることがあります。
calleeオブジェクト自体が生きていても、 MyCallerコンストラクタのポインタ変数pはコンストラクタから抜けた段階で寿命がきます。 なのでコンストラクタの外でpを参照キャプチャした関数オブジェクトを呼び出すと寿命が尽きたpを参照することになります。 pの参照先のcalleeオブジェクトは生きていますが、p自体が寿命という訳です。
ポインタの参照先の寿命を気にするあまりポインタ変数の寿命を見落としてしまうという失敗でした。
最後に実行結果を載せます。

$ Debug\Project1.exe
1

$ Release\Project1.exe
1

$ x64\Debug\Project1.exe
1
2

$ x64\Release\Project1.exe
1
2

x86ビルドでは2を出力する前にプログラムが落ちました。 x64ビルドではアクセスエラーが発生せずcaller.call2();が処理されてます。 何ででしょう。 今までの説明が合っているか自信なくなりました。

参考

cpprefjpによると「ラムダ式がひとつ以上の変数を参照キャプチャしている場合、参照している変数の寿命が切れたあとの、ラムダ式のコピーと呼び出しの動作は未定義」とのことです。

cpprefjp.github.io