2017-07-26 34 views
1

這個例子顯示編譯器(msvc14,gcc,clang)的奇怪行爲,但是我沒有找到解釋。不是默認的析構函數導致不完整的類型錯誤

當我們實現pipml習語並使用前向聲明時,我們需要考慮unique_ptr具有自己的具有不完整類型的特定行爲。 這種情況被提及herehere

但是,當我們將轉發類的定義移動到另一個頭文件並在稍後使用客戶類時在一個地方包含頭文件時,編譯器變得瘋狂 - 在某些特殊的析構函數聲明中,他們說關於不完整類型。

這是一個簡單的例子。如果取消註釋「#define CASE_2」或「#define CASE_3」並嘗試構建它,則會出現編譯錯誤。

文件foo.h中

#ifndef FOO_H 
#define FOO_H 

class Foo{}; 

#endif // FOO_H 

文件base.h

#ifndef BASE_H 
#define BASE_H 

#include <memory> 

//#define CASE_1 
//#define CASE_2 
//#define CASE_3 

class Foo; 

class Base 
{ 
public: 

#if defined(CASE_1) 
    ~Base() = default; // OK! 
#elif defined(CASE_2) 
    ~Base() {}; // error: invalid application of 'sizeof' to incomplete type 'Foo' 
#elif defined(CASE_3) 
    ~Base(); // error: invalid application of 'sizeof' to incomplete type 'Foo' 
#endif 

    // OK! 

private: 
    std::unique_ptr<Foo> m_foo; 
}; 

#endif // BASE_H 

文件base.cpp

#include "base.h" 

#if defined(CASE_3) 
Base::~Base() 
{ 
} 
#endif 

文件main.cpp中

#include "foo.h" // No matter order of this includes 
#include "base.h" // 

int main() 
{ 
    Base b; 
} 
+1

不確定,但我認爲編譯器實際上並沒有抱怨析構函數,而是關於默認的構造函數,如果你聲明瞭非默認的析構函數,則缺少 – user463035818

+0

如果你從基地包含foo.h,CASE_3編譯得很好。 CPP,無論如何,你應該這樣做。如果你不包含main.cpp中的foo.h,那麼'CASE_1'不會編譯,你不應該這樣做。 – Slava

回答

1

我相信,它具有與C++標準12.4/6.

被默認併爲已刪除不限定的析構函數做當ODR使用的是 隱含地定義(3.2)至破壞的目的 其類別類型(3.7)或者在其第一次聲明後明確默認爲 。

當你有你的析構函數違約,它只會定義時使用的ODR,即當Base對象被銷燬。在你的代碼片段中,沒有任何類型的對象會被銷燬,因此程序編譯 - 因爲unique_ptr的deleter實際上並沒有在任何地方被調用 - 它只能被Base析構函數調用,這在本場景中沒有定義。

當您提供用戶定義的析構函數時,它被就地定義,並且程序會變形,因爲您不能破壞不完整類型的對象unique_ptr

順便說一下,具有析構函數declared,但不是defined(如~base();)不會由於同樣的原因而導致編譯錯誤。

+0

我不確定「這種類型的物體是否被破壞」,我們有「Base b;」在main.cpp中,當它超出範圍時它應該被銷燬。我猜。 – degreeme

+0

@degreeme,是的,但** AFTER **中包含了'Foo'的定義。不在'Base'的頭部。 – SergeyA

0

但是,當我們在一個地方與客戶端類的使用移動轉發類的定義,另一頭文件,包括頭後,編譯器變得瘋狂 - 在析構函數的一些特殊情況下,他們說,大約不完整的類型。

編譯器都很好,Foo類定義的析構函數B定義的時候必須是可見的了。在CASE_1中發生這種情況 - 在main.cpp中定義了析構函數,並且在那裏包含foo.h。CASE_2不會編譯,不應該使用。 CASE_3會在您從base.cpp包括foo.h時編譯,無論如何您都應該這樣做,並使用此案例(並且不包括來自main.cpp的foo.h,否則您將打破pimpl習語的全部目的)。

因此,沒有編譯器的奇怪行爲,您使用pimpl習語很奇怪,會導致您觀察到的行爲。

相關問題