unique_ptr 的模板实例化和 pimpl 习语

Template instantiations and pimpl idiom with unique_ptr

我读了 Howard Hinnant (Is std::unique_ptr<T> required to know the full definition of T?) and then this answer (How is a template instantiated?) 的这个回答,我只是在想。如果你有这样的 class

class Something {
    Something(); 
    ~Something();
    class Impl;
    std::unique_ptr<Impl> impl;
};

unique_ptr 将在编译 class 时实例化(我可以从上面的其他答案中看出)。那为什么以后不定义 class Impl 就可以了?实例化不需要 Impl 的析构函数存在吗?

注意以下是为了澄清我上面的问题。

我的想法是,当编译器检查 class Something 的定义时。它将看到嵌套的 class Impl 的声明,然后它会看到 unique_ptr<Impl> 的声明,然后在那个位置。它将用 Impl 实例化模板 unique_ptr。该实例化代码将包含对 Impl 的析构函数的调用。由于此时我们的代码包含对不完整 class 的析构函数的调用,上面的代码如何安全?

The accepted answer to the first question 包含 table 个用例,其中需要 Impl 的完整定义。

在您的情况下,编译器隐式生成以下成员函数:

  • 复制构造函数
  • 移动构造函数
  • 复制赋值运算符
  • 移动赋值运算符。

他们都需要Impl的完整定义。

如果您显式声明这些函数并在 Impl 的完整定义可用的地方定义它们,您就没问题。

更新

It will see the declaration of the nested class Impl and then it will see the declaration of the unique_ptr<Impl>, and at that point. It will instantiate the template unique_ptr with Impl.

这只是在一定程度上是正确的。不是所有unique_ptr的成员函数都会在那个时候被实例化。

And that instantiated code will contain a call to the destructor of Impl.

不正确。只有在需要时,编译器才会生成(或实例化)std::unique_ptr<Impl> 的析构函数的代码。那个地方就是Something.

的析构函数

Something 的析构函数将需要 std::unique_ptr<Impl> 的析构函数。
std::unique_ptr<Impl> 的析构函数需要 Impl.

的完整定义

换句话说,Impl的完整定义必须对Something的析构函数可见。

PS

有关模板实例化的更多信息,请访问 Template instantiation details of GCC and MS compilers

如果你尝试编译上面的代码并创建一个Something的对象,它会给出一个错误信息:

Semantic issue:
memory:2523:27: Invalid application of 'sizeof' to an incomplete type 
'Something::Impl'

简而言之,代码是不可编译的,在这种情况下没有必要考虑安全问题。