22

首先阅读 Herb 的 Sutters GotW 关于 C++11 中 pimpl 的帖子:

我在理解 GotW #101 中提出的解决方案时遇到了一些麻烦。据我所知,GotW #100 中辛苦解决的所有问题都卷土重来:

  • 成员是离线模板,并且定义在pimpl使用点不可见(在class widget的类定义和隐式生成的特殊成员函数中widget)。也没有任何明确的实例化。这将在链接期间导致未解决的外部错误。

  • widget::impl在定义实例化的地方仍然不完整pimpl<widget::impl>::~pimpl()(我认为它实际上根本没有实例化,只是被引用)。因此调用指向不完整类型的指针,如果具有非平凡的析构函数,则会产生未定义的行为。std::unique_ptr<widget::impl>::~unique_ptr()deletewidget::impl

请解释是什么迫使编译器在完整的上下文中生成特殊成员widget::impl。因为我看不出这是如何工作的。


widget::~widget()如果 GotW #101 仍然需要在实现文件中明确定义widget::impl,那么请解释“更强大”的评论(@sehe 在他的回答中引用)。

我正在查看 GotW #101 的核心主张,即包装“消除了一些样板文件”,在我看来(基于本段的其余部分)意味着widget::~widget()声明和定义。所以请不要在你的答案中依赖它,在 GotW #101 中,那已经消失了!


Herb,如果您路过,请让我知道是否可以在此处剪切+粘贴解决方案代码以供参考。

4

2 回答 2

10

你是对的; 该示例似乎缺少显式模板实例化。当我尝试在 MSVC 2010 SP1 上运行带有构造函数和析构函数的示例时widget::impl,我得到一个pimpl<widget::impl>::pimpl()和的链接器错误pimpl<widget::impl>::~pimpl()。当我添加template class pimpl<widget::impl>;时,它链接正常。

换句话说,GotW #101 消除了 GotW #100 中的所有样板,但您需要添加pimpl<...>模板的显式实例化和pimplimpl 的实现。至少对于#101,你需要的样板是简单的。

于 2011-12-21T19:53:32.673 回答
7

我认为混淆是这样的:pimpl 包装器可能是一个模板,小部件类不是:

demo.h

#include "pimpl_h.h"

// in header file
class widget {
public:
    widget();
    ~widget();
private:
    class impl;
    pimpl<impl> pimpl_;
};

演示.cpp

#include "demo.h"
#include "pimpl_impl.h"

// in implementation file
class widget::impl {
    // :::
};

widget::widget() : pimpl_() { }
widget::~widget() { } // or =default

如您所见,没有人会看到小部件类的“模板化”构造函数。它只有一个定义,不需要“显式实例化”。

相反,~pimpl<>析构函数仅从析构函数单点定义实例化~widget()。根据定义,此时impl该类已完成。

没有链接错误,也没有 ODR/UB 违规。

奖金/额外福利

正如 Herb 本人在他的帖子中恰当地解释的那样(为什么这是对手动 Pimpl Idiom 的改进?1),使用这个 pimpl 包装器还有更多的优势,这源于重用实现的胆量:

  • 使用模板防止不必要地陷入陷阱
  • 您将获得具有 C++0x/C++11 的可变参数、完美的转发构造函数,无需梦想具有右值引用可变参数列表的模板化构造函数模板将参数包完美地转发到包装类的构造函数等等。

简而言之:干燥和方便。


1引用(强调我的):

  • 首先,代码更简单,因为它消除了一些样板:在手动版本中,您还必须声明构造函数并将其主体写入实现文件并显式分配 impl 对象。您还必须记住声明析构函数并将其主体写入实现文件,原因在 #100 中解释了晦涩的语言原因。
  • 其次,代码更健壮:在手卷版本中,如果忘记编写外联析构函数,Pimpl'd 类将单独编译,并且看起来处于可签入状态,但是当被试图销毁对象的调用者使用并遇到有用的“无法生成析构函数,因为 impl 是,呃,你知道,不完整”的错误时编译失败,这让调用代码的作者在走路时摸不着头脑到你的办公室把你拿出来检查一些坏了的东西。
于 2011-12-21T20:21:50.633 回答