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 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_assert
para 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).