2017-04-13 66 views
2

我读这个答案霍华德Hinnant(Is std::unique_ptr<T> required to know the full definition of T?),然后这个答案(How is a template instantiated?),我只是在想。如果你有像这样模板实例化和pimpl成语与unique_ptr

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

unique_ptr类时编译将在此时被实例化一个类(因为我可以从上面的其他答案做出来)。那么为什么以后没有定义类Impl呢?实例化是否需要Impl的析构函数存在?

注意以下是努力澄清我在上面提出的问题。

我在想这个问题,当编译器检查类Something的定义时。它会看到嵌套类Impl的声明,然后它将看到unique_ptr<Impl>的声明,并在那一点上。它将用Impl实例化模板unique_ptr。并且该实例化的代码将包含对Impl的析构函数的调用。因为在这一点上,我们的代码包含对不完整类的析构函数的调用,上面的代码如何安全?

回答

1

The accepted answer to the first question包含需要完整定义Impl的用例表。

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

  • 复制构造
  • 移动构造
  • 拷贝赋值运算符
  • 移动赋值运算符。

所有这些都需要Impl的完整定义。

如果您明确声明这些函数并在Impl的完整定义可用的情况下定义它们,那么您将可以。

更新

将看到嵌套类Impl的声明,然后将看到unique_ptr<Impl>的声明,在这一点上。它将用Impl实例化模板unique_ptr

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

并且该实例化的代码将包含对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找到。

+0

成员函数为哪个类?我还看到了第一个问题的答案,但仍然不明白,完整的'unique_ptr'类将在代码中的指定类型(指向impl的指针的声明)中实例化,并且该实例化将是调用'Impl'的析构函数的代码,但这个代码还不可见。那么代码是如何编译的? – Curious

+0

@Curious,在链接的答案中,“P”是“shared_ptr”或“unique_ptr”。在你的情况下,隐式生成的拷贝构造函数将调用'unique_ptr '的拷贝构造函数。将该逻辑扩展到其他编译器生成的类的成员函数。 –

+0

我不认为我正在传达我的想法,我会尝试澄清在 – Curious

0

如果写尝试编译上面的代码,有所创造的一个对象,它会给出一个错误信息:

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

总之代码是不可编译的,也没有在考虑有关安全点这个案例。