2016-11-17 10 views
2

Следующий код корректно компилируется с Visual Studio 2013:Почему для конструктора по умолчанию требуется дефолт по умолчанию для класса, используемого в unique_ptr?

#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, в котором инициализация mc в main непосредственно оптимизированной без проверки конструктора перемещения.

В 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

Я не могу сказать, почему (хороший вопрос, хотя). вы можете исправить это, когда у вас есть MyClass :: MyClass (MyClass &&) = default; 'в вашем файле cpp и только объявление в файле заголовка. – Hayt

+2

Не можете ли вы написать 'NS :: MyClass mc;' в 'main()'? – alain

ответ

2

std::unique_ptr требуется полный тип, в частности для обработки удаления.

Ваш дефолте конструктор шаг инлайн и может быть представлен следующий псевдокод:

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

Это требует SomeOtherClass быть полным типа, чтобы иметь возможность перемещать Deleter по умолчанию шаблонного на нем.

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, «другой» экземпляр будет содержать nullptr, и хотя удаление nullptr не требует деструктора, реализация деструктора unique_ptr будет по-прежнему содержать код для вызова деструктора. Поэтому, хотя в этом случае он никогда не вызывается, это необходимо. – Patrick

+0

@pat Нет, это не так, переместить не уничтожает источник, он просто отменяет его. – Yakk

+0

@krz no, это не разрешенный конструктор перемещения. Это должен быть MyClass (MyClass && other): m_someOtherClass (std :: move (other.m_someOtherClass)) {} '. И даже в move-ctor, движок ctor не нуждается в '.reset', поэтому я не считаю, что это разрешено ... – Yakk

 Смежные вопросы

  • Нет связанных вопросов^_^