E' qualcosa al limite dell'UB. All'atto pratico alcuni compilatori (non so se tutti) permettono di invocare un metodo attraverso un puntatore non valido PURCHE' tale metodo non acceda a variabili membro (si presume che tale comportamento abbia a che fare con ralune ottimizzazioni).
Se vuoi un UB basta che la funzione f() di Foo acceda a un dato membro (ad es. un int data).
codice:struct Foo { ~Foo() { cout << "~Foo()\n"; } void f() const { data = 0; cout << "Foo:f()\n"; } // BOOM! int data; };