El siguiente código compila y enlaza con Visual Studio
(tanto 2017 como 2019 con /permissive-
), pero no compila con ninguno gcc
o 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::Bar
debe 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 Studio
no está de acuerdo y acepta este código. Además, descubrí que los siguientes cambios hacen que Visual Studio
rechace el código:
- Hacer
(1)
no virtual - Definición en
(2)
línea - es decirFoo() = default;
oFoo(){};
- Quitando
(3)
Me parece Visual Studio
que 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 Studio
hace 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 a std::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*/}}