首先阅读Herb's 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()调用delete指向不完整类型的指针,如果widget::impl有一个非平凡的析构函数,则会产生未定义的行为.
请解释是什么迫使编译器在widget::impl完成的上下文中生成特殊成员.因为我看不出它是如何工作的.
如果GotW#101仍然需要widget::~widget()在实现文件中明确定义,哪里widget::impl完成,那么请解释"更健壮"的评论(@sehe在他的答案中引用).
我看的GotW#101的核心要求是,包装"消除样板的一些作品",这在我看来(基于该段的其余部分)来表示的widget::~widget()声明和定义.所以请不要依赖于你的答案,在GotW#101中,那已经消失了!
Herb,如果你停下来,请告诉我是否可以在这里剪切+粘贴解决方案代码以供参考.
我写了这篇文章并得到了一些让我困惑的评论.
它基本上归结为我已经看到T2仅用作模板参数并错误地跳到结论我因此可以利用前向声明的机会:
struct T2;
struct T1
{
std::auto_ptr<T2> obj;
};
Run Code Online (Sandbox Code Playgroud)
如果我不继续在T2同一个TU中定义某个地方,则调用UB ,因为std::auto_ptr<T2>调用delete其内部T2*,并调用delete指向不完整类型的对象的指针,其完整类型具有非平凡的析构函数是未定义的:
[C++11: 5.3.5/5]:如果被删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为是未定义的.
我碰巧使用的GCC工具链 - v4.3.3(Sourcery G ++ Lite 2009q1-203) - 非常友好地通过一个注释让我知道:
注意:即使在定义类时声明析构函数也不会调用析构函数或特定于类的运算符delete.
虽然在其他GCC版本中似乎很难得到这种诊断.
我的抱怨是,如果delete指向不完整类型的实例的指针是不正确的而不是UB,那么发现这样的bug会容易得多,但这似乎是一个实现难以解决的问题,所以我明白为什么是UB.
但后来我被告知,如果我使用它std::unique_ptr<T2>,这将是安全和合规的.
据称n3035在20.9.10.2说:
模板参数
T的unique_ptr可能是一个不完整的类型.
我在C++ 11中找到的所有内容都是:
[C++11: 20.7.1.1.1]:/ 1类模板
default_delete用作类模板的默认删除器(销毁策略)unique_ptr./ 2模板参数
T的default_delete可能是一个不完整的类型.
但是,default_delete的operator()的确需要一个完整的类型:
[C++11: …