为什么默认移动构造函数需要 unique_ptr 中使用的 class 的默认删除器?

Why does default move constructor need default-deleter of class used in unique_ptr?

以下代码使用 Visual Studio 2013 正确编译:

#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 中的一个错误,其中 mcmain 中的初始化直接优化,而不检查移动构造函数。

在 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 的定义?

std::unique_ptr需要一个完整的类型,专门用来处理删除。

您的默认移动构造函数是内联的,可以用以下伪代码表示:

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

这需要SomeOtherClass是一个完整的类型,才能移动模板化的默认删除器。

MSDN on defining inline C++ functions with dllexport

You can define as inline a function with the dllexport attribute. In this case, the function is always instantiated and exported, whether or not any module in the program references the function. The function is presumed to be imported by another program.

我手边没有 VS2015,但只需在 class 中声明构造函数并在定义 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;