コンパイラは、暗黙的に宣言された仮想デストラクタの実装を単一の個別の翻訳単位に配置できますか?


8

次のコードは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.cppFoo::Barその削除を試みているので、完全な型である必要があり~Foo()、暗黙的に宣言されているため、暗黙的にアクセスするすべての翻訳単位で定義されています。

ただし、Visual Studio同意せず、このコードを受け入れます。さらに、次の変更によりVisual Studioコードが拒否されることがわかりました。

  • 作る(1)非仮想
  • (2)インラインの定義-つまりFoo() = default;またはFoo(){};
  • 削除しています (3)

Visual Studio次の条件で使用されるすべての場所で暗黙のデストラクタが定義されていないように見えます。

  • 暗黙のデストラクタは仮想です
  • クラスに、別の翻訳単位で定義されたコンストラクターがある

代わりに、2番目の条件のコンストラクターの定義も含む翻訳単位でデストラクターのみを定義しているようです。

だから今私は思っています:

  • これは許可されますか?
  • これはどこに指定されていますか、それとも少なくともわかってVisual Studioいますか?

更新:バグレポートhttps://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.htmlを提出しました。専門家がこれをどうするか見てみましょう。


1
/ permissive-スイッチを使用してVisual Studioでコードをビルドするとどうなりますか?
Jesper Juhl、

1
同じ結果。それを質問に入れます。
マーク・

1
変更2と3は明らかです。(デフォルトの)deleterが呼び出されたときに完全な型が必要です(unique_ptrのデストラクタで、これはFooのコンストラクタで再び発生するため、後者がインラインの場合、型はすでにヘッダーで完全である必要があります)変更1は驚きですが、説明はありません。
アコンカグア

これをFoo:に追加しstruct BarDeleter { void operator()(Bar*) const noexcept; };、unique_ptrをに変更しstd::unique_ptr<Bar, BarDeleter> bar_;ます。次に、実装翻訳ユニットに追加しますvoid Foo::BarDeleter::operator()(Foo::Bar* p) const noexcept { try { delete p; } catch(...) {/*discard*/}}
Eljay

回答:


2

これはMSVCのバグだと思います。に関しては、std::default_delete::operator()標準はそれを[unique.ptr.dltr.dflt / 4]と言っています

備考: Tが不完全な型である場合、プログラムは不正な形式です。

全く存在しないので、「なし診断必須」句は、準拠C ++コンパイラは、診断を発行する必要がある [intro.compliance / 2.2]

プログラムに診断可能なルールまたは...の違反が含まれている場合、適合実装は少なくとも1つの診断メッセージを発行します

[intro / compliance / 1]と一緒に:

診断可能なルールのセットは、「診断が必要ない」、または「未定義の動作」を引き起こすと説明されている明示的な表記を含むルールを除き、このドキュメントのすべての構文ルールと意味ルールで構成されます。


GCCはstatic_assert型の完全性を診断するために使用します。MSVCはそのようなチェックを実行していないようです。暗黙的にstd::default_delete::operator()toのパラメーターを渡すと、未定義の動作がdelete発生します。あなたの観察と一致するかもしれません。動作する可能性がありますが、ドキュメントで(非標準のC ++拡張として)保証されるまでは、使用しません。


これも今のところ私の推論です。
マーク・

1
@DanielLangr OHHHH [@ $%*&+!]完全に見過ごさています。デストラクタではなく、提供さているのはデフォルトのコンストラクタです!!! 基本クラスに仮想デストラクタがあると、すでに私は追い出されています... すみません。もちろん、あなたは完全に正しいです。dtorの代わりにctorを提供することが意図的または偶然だったのかどうか疑問に思っています...
アコンカグア

1
@アコンカグア、それは意図的です。問題は、msvcが使用されていない翻訳単位で暗黙のデストラクタを定義し、それが使用されている翻訳単位でそれを定義しないことです。
マーク・

1
@DanielLangrリンクをありがとう...しかし、デフォルトのコンストラクターにも意味があります。複雑なメンバーを持つクラスを検討してください:class Demo { std::vector<int> data; };
Aconcagua

1
@アコンカグアついに理解できたと思います。問題はのデフォルトのコンストラクタにはありませんstd::unique_ptr<Bar>。問題は、のデフォルトのコンストラクタにありFooます。例外がある場合は、Foo::Foo()構築済みのサブオブジェクトを破棄する必要がありますbar_(ロールバック)。
Daniel Langr
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.