O código a seguir compila e vincula com Visual Studio
(2017 e 2019 com /permissive-
), mas não compila com gcc
ou 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>(); }
Meu entendimento é que, em main.cpp
, Foo::Bar
deve ser um tipo completo, porque sua exclusão é tentada ~Foo()
, o que é implicitamente declarado e, portanto, implicitamente definido em todas as unidades de tradução que o acessam.
No entanto, Visual Studio
não concorda e aceita esse código. Além disso, descobri que as seguintes alterações Visual Studio
rejeitam o código:
- Tornando
(1)
não virtual - Definindo
(2)
inline -Foo() = default;
ou seja,Foo(){};
- Removendo
(3)
Parece-me Visual Studio
que não define um destruidor implícito em todos os lugares em que é usado nas seguintes condições:
- O destruidor implícito é virtual
- A classe possui um construtor definido em uma unidade de tradução diferente
Em vez disso, parece definir apenas o destruidor na unidade de conversão que também contém a definição para o construtor na segunda condição.
Então agora eu estou me perguntando:
- Isso é permitido?
- É especificado em algum lugar, ou pelo menos conhecido, que
Visual Studio
faz isso?
Atualização: arquivei um relatório de bug https://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.html . Vamos ver o que os especialistas acham disso.
struct BarDeleter { void operator()(Bar*) const noexcept; };
e altere o unique_ptr para std::unique_ptr<Bar, BarDeleter> bar_;
. Em seguida, na unidade de tradução da implementação, adicionevoid Foo::BarDeleter::operator()(Foo::Bar* p) const noexcept { try { delete p; } catch(...) {/*discard*/}}