Der folgende Code kompiliert und verknüpft mit Visual Studio
(sowohl 2017 als auch 2019 mit /permissive-
), kompiliert jedoch nicht mit entweder gcc
oder 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>(); }
Mein Verständnis ist , dass, in main.cpp
, Foo::Bar
muß eine vollständige Typ sein, weil deren Löschung in versucht wird ~Foo()
, die implizit deklariert und somit implizit in jeder Übersetzungseinheit definiert , die es zugreift.
Ist Visual Studio
jedoch nicht einverstanden und akzeptiert diesen Code. Außerdem habe ich festgestellt, dass die folgenden Änderungen dazu führen, dass Visual Studio
der Code abgelehnt wird:
- Herstellung von
(1)
nicht-virtuellen (2)
Inline definieren - dhFoo() = default;
oderFoo(){};
- Entfernen
(3)
Es sieht für mich so aus Visual Studio
ein impliziter Destruktor nicht überall dort definiert, wo er unter den folgenden Bedingungen verwendet wird:
- Der implizite Destruktor ist virtuell
- Die Klasse verfügt über einen Konstruktor, der in einer anderen Übersetzungseinheit definiert ist
Stattdessen scheint es nur den Destruktor in der Übersetzungseinheit zu definieren, der auch die Definition für den Konstruktor in der zweiten Bedingung enthält.
Jetzt frage ich mich also:
- Ist das erlaubt?
- Ist irgendwo angegeben oder zumindest bekannt, dass dies der
Visual Studio
Fall ist?
Update: Ich habe einen Fehlerbericht https://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.html eingereicht . Mal sehen, was die Experten daraus machen.
quelle
struct BarDeleter { void operator()(Bar*) const noexcept; };
und ändern Sie den unique_ptr instd::unique_ptr<Bar, BarDeleter> bar_;
.void Foo::BarDeleter::operator()(Foo::Bar* p) const noexcept { try { delete p; } catch(...) {/*discard*/}}
Antworten:
Ich glaube, das ist ein Fehler in MSVC. Wie für das
std::default_delete::operator()
, sagt die Norm , dass [unique.ptr.dltr.dflt / 4] :Da es keine Klausel "Keine Diagnose erforderlich" gibt , muss ein konformer C ++ - Compiler eine Diagnose ausgeben [intro.compliance / 2.2] :
zusammen mit [Intro / Compliance / 1] :
GCC verwendet,
static_assert
um die Typvollständigkeit zu diagnostizieren. MSVC führt anscheinend keine solche Prüfung durch. Wenn ein Parameter vonstd::default_delete::operator()
to stillschweigend übergeben wird, führtdelete
dies zu undefiniertem Verhalten . Welches könnte mit Ihrer Beobachtung korrespondieren. Es mag funktionieren, aber bis es durch die Dokumentation (als nicht standardmäßige C ++ - Erweiterung) garantiert wird, würde ich es nicht verwenden.quelle
class Demo { std::vector<int> data; };
std::unique_ptr<Bar>
. Das Problem ist mit dem Standardkonstruktor vonFoo
. Wenn es eine Ausnahme gibt,Foo::Foo()
muss das bereits erstellte Unterobjekt zerstört werdenbar_
(Rollback).