2016-11-17 76 views
2

下面的代碼正確使用Visual Studio 2013編譯:爲什麼默認移動構造函數需要unique_ptr中使用的類的default-deleter?

#include <memory> 

namespace NS 
    { 
    class SomeOtherClass; 

    class MyClass 
     { 
     public: 
     MyClass(); 
     virtual ~MyClass(); 
     private: 
     std::unique_ptr<SomeOtherClass> m_someOtherClass; 
     }; 
    } 

int main() 
    { 
    auto mc = NS::MyClass(); 
    } 

這是因爲在Visual Studio 2013中的錯誤,其中在mainmc初始化,直接,不檢查一個移動構造函數優化。

在Visual Studio 2015年,這並不編譯,因爲移動構造函數必須存在的,所以我們的代碼改成這樣:

#include <memory> 

namespace NS 
    { 
    class SomeOtherClass; 

    class MyClass 
     { 
     public: 
     MyClass(); 
     virtual ~MyClass(); 
     MyClass(MyClass&&) = default; 
     private: 
     std::unique_ptr<SomeOtherClass> m_someOtherClass; 
     }; 
    } 

int main() 
    { 
    auto mc = NS::MyClass(); 
    } 

,這再次編譯。

但是,如果我們現在要導出DLL,那麼編譯再次失敗。這是修改後的代碼:

#include <memory> 

namespace NS 
    { 
    class SomeOtherClass; 

    class __declspec(dllexport) MyClass 
     { 
     public: 
     MyClass(); 
     virtual ~MyClass(); 
     MyClass(MyClass&&) = default; 
     private: 
     std::unique_ptr<SomeOtherClass> m_someOtherClass; 
     }; 
    } 

int main() 
    { 
    auto mc = NS::MyClass(); 
    } 

這是編譯器輸出的一部分:

memory(1193): error C2027: use of undefined type 'NS::SomeOtherClass' 
test.cpp(5): note: see declaration of 'NS::SomeOtherClass' 
... 
memory(1194): error C2338: can't delete an incomplete type 
memory(1195): warning C4150: deletion of pointer to incomplete type 'NS::SomeOtherClass'; no destructor called 

似乎默認生成的舉動,構造函數需要能夠破壞SomeOtherClass。這很奇怪,因爲MyClass有一個析構函數,其中SomeOtherClass的完整定義是已知的。

那麼爲什麼導出DLL時不編譯?爲什麼默認的移動構造函數需要知道SomeOtherClass的定義?

+0

我不能告訴你爲什麼(雖然很好的問題)。你可以在你的cpp文件中有MyClass :: MyClass(MyClass &&)= default;'並且只有頭文件中的聲明時修復這個問題。 – Hayt

+2

你不能在'main()'中寫'NS :: MyClass mc'嗎? – alain

回答

2

std::unique_ptr需要一個完整的類型,特別是處理刪除。

你拖欠移動構造函數是inline和可以由以下僞代碼來表示:

MyClass(MyClass&& other): 
    m_someOtherClass(std::move(other.m_someOtherClass)); 
{} 

這需要SomeOtherClass是一個完整的類型,可以移動模板默認就可以刪除器。

MSDN上限定內聯的C++函數與DLLEXPORT

可以爲內聯定義與dllexport屬性的功能。在 這種情況下,函數總是被實例化和導出,無論是 還是不是程序中的任何模塊引用該函數。推測功能 是由另一個程序導入的。

我沒有VS2015方便,但剛宣佈在類的構造函數,並在SomeOtherClass定義應該做的伎倆翻譯單元定義它:

class __declspec(dllexport) MyClass 
     { 
     public: 
     MyClass(); 
     virtual ~MyClass(); 
     MyClass(MyClass&&); 
     private: 
     std::unique_ptr<SomeOtherClass> m_someOtherClass; 
     }; 
    } 

file_containing_〜MyClass.cpp

MyClass::MyClass(MyClass&&)=default; 
+0

在您回答之前,我的假設是SomeOtherClass不必被破壞。但正如你指出的那樣,它的確如此。在unique_ptr發佈之後,'other'實例將包含一個nullptr,儘管刪除一個nullptr不需要析構函數,但unique_ptr的析構函數的實現仍將包含調用析構函數的代碼。所以雖然在這種情況下它從來沒有被調用過,但它是需要的。 – Patrick

+0

@pat不,它不是,移動不會破壞源,它只是將它置之不理。 – Yakk

+0

@krz不,這不是一個允許的移動構造函數。它必須是MyClass(MyClass && other):m_someOtherClass(s​​td :: move(other.m_someOtherClass)){}'。即使在移動中,移動ctor也不需要'.reset',所以我不認爲這是被允許的...... – Yakk

相關問題