Может ли компилятор поместить реализацию неявно объявленного виртуального деструктора в один отдельный модуль перевода? - PullRequest
7 голосов
/ 22 октября 2019

Следующий код компилируется и связывается с Visual Studio (как 2017, так и 2019 с /permissive-), но не компилируется с gcc или 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>();
}

Насколько я понимаю, в main.cpp, Foo::Bar необходимобыть полным типом, потому что попытка его удаления выполняется в ~Foo(), который неявно объявляется и, следовательно, неявно определяется в каждой единице перевода, которая обращается к нему.

Однако Visual Studio не соглашается и принимает этот код,Кроме того, я обнаружил, что следующие изменения заставляют Visual Studio отклонять код:

  • Делает (1) не виртуальным
  • Определение (2) встроенным - то есть Foo() = default; или Foo(){};
  • Удаление (3)

Мне кажется, что Visual Studio не определяет неявный деструктор везде, где он используется при следующих условиях:

  • Неявный деструктор является виртуальным
  • В классе есть конструктор, который определен в другой единице перевода

Вместо этого, кажется, он определяет деструктор только вмодуль перевода, который также содержит определение конструктора во втором условии.

Так что теперь мне интересно:

  • Это разрешено?
  • Указано ли где-либо или, по крайней мере, известно, что Visual Studio это делает?

Обновление: Я отправил отчет об ошибке https://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.html. Давайте посмотрим, что из этого делают эксперты.

1 Ответ

2 голосов
/ 22 октября 2019

Я считаю, что это ошибка в MSVC. Что касается std::default_delete::operator(), то в стандарте говорится, что [unique.ptr.dltr.dflt / 4] :

Примечания: Если T являетсянеполный тип, программа некорректно сформирована .

Поскольку отсутствует «нет необходимости в диагностике» предложение, соответствующий компилятор C ++ требуется для выдачи диагностики [intro.compliance / 2.2] :

Если программа содержит нарушение какого-либо диагностируемого правила или .. ., соответствующая реализация должна выдать хотя бы одно диагностическое сообщение .

вместе с [intro / compatibility / 1] :

Набор диагностируемых правил состоит из всех синтаксических и семантических правил в этом документе, за исключением тех правил, которые содержат явное обозначение, что «диагностика не требуется» или которые описаны как приводящие к «неопределенному поведению».


GCC использует static_assert для диагностики полноты типа. MSVC, по-видимому, не выполняет такую ​​проверку. Если он молча передает параметр от std::default_delete::operator() до delete, то это вызывает неопределенное поведение . Что может соответствовать вашему наблюдению. Это может работать, но пока это не гарантировано документацией (как нестандартное расширение C ++), я бы не стал его использовать.

...