El siguiente código compila y enlaza con Visual Studio(tanto 2017 como 2019 con /permissive-), pero no compila con ninguno gcco clang.
foo.h
#include <memory> struct Base { virtual ~Base() = default; // (1) }; struct Foo : public Base { Foo(); // (2) struct Bar; std::unique_ptr<Bar> bar_; };foo.cpp
#include "foo.h" struct Foo::Bar {}; // (3) Foo::Foo() = default;main.cpp
#include "foo.h" int main() { auto foo = std::make_unique<Foo>(); }
Tengo entendido que, en main.cpp, Foo::Bardebe ser un tipo completo, porque se intenta eliminarlo ~Foo(), lo que se declara implícitamente y, por lo tanto, se define implícitamente en cada unidad de traducción que accede a él.
Sin embargo, Visual Studiono está de acuerdo y acepta este código. Además, descubrí que los siguientes cambios hacen que Visual Studiorechace el código:
- Hacer
(1)no virtual - Definición en
(2)línea - es decirFoo() = default;oFoo(){}; - Quitando
(3)
Me parece Visual Studioque no define un destructor implícito en todas partes donde se usa bajo las siguientes condiciones:
- El destructor implícito es virtual
- La clase tiene un constructor que se define en una unidad de traducción diferente.
En cambio, parece solo definir el destructor en la unidad de traducción que también contiene la definición del constructor en la segunda condición.
Entonces ahora me pregunto:
- ¿Esto está permitido?
- ¿Se especifica en alguna parte, o al menos se sabe, que
Visual Studiohace esto?
Actualización: he presentado un informe de error https://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.html . Veamos qué piensan los expertos de esto.

struct BarDeleter { void operator()(Bar*) const noexcept; };y cambie el unique_ptr astd::unique_ptr<Bar, BarDeleter> bar_;. Luego, en la unidad de traducción de implementación, agreguevoid Foo::BarDeleter::operator()(Foo::Bar* p) const noexcept { try { delete p; } catch(...) {/*discard*/}}Respuestas:
Creo que esto es un error en MSVC. En cuanto a
std::default_delete::operator(), el Estándar dice que [unique.ptr.dltr.dflt / 4] :Como no hay una cláusula de "no se requiere diagnóstico" , se requiere un compilador C ++ conforme para emitir un diagnóstico [intro.compliance / 2.2] :
junto con [introducción / cumplimiento / 1] :
GCC utiliza
static_assertpara diagnosticar la integridad del tipo. MSVC aparentemente no realiza tal verificación. Si pasa silenciosamente un parámetro destd::default_delete::operator()adelete, esto provoca un comportamiento indefinido . Lo que podría corresponder con su observación. Puede funcionar, pero hasta que no esté garantizado por la documentación (como una extensión de C ++ no estándar), no lo usaría.fuente
class Demo { std::vector<int> data; };std::unique_ptr<Bar>. El problema es con el constructor predeterminado deFoo. Si hay una excepción,Foo::Foo()debe destruir el subobjeto ya construidobar_(reversión).