什么时候被认为是完整的?

时间:2014-07-19 13:47:41

标签: c++ boost smart-pointers incomplete-type scoped-ptr

请考虑以下代码段。 boost :: scoped_ptr的析构函数在main函数的末尾调用。析构函数使用boost :: checked_delete来解除分配封装的Widget指针。

#include <boost/scoped_ptr.hpp>
#include <iostream>

class Widget;
Widget *make_widget();

int main()
{  
  boost::scoped_ptr<Widget> sp(make_widget());
  // std::cout << sizeof(Widget) << std::endl;
}

class Widget
{
public:
  Widget() {}
  ~Widget() { std::cout << "Widget destructor called." << std::endl; }
};

Widget *make_widget()
{
  return new Widget;
}

我希望这段代码无法编译,因为类Widget在调用scoped_ptr<Widget>的析构函数时是不完整的。但是,这可以在g ++ 4.8和Visual Studio 2010上完全编译。请注意main函数中带有sizeof(Widget)表达式的注释语句。如果我取消注释,它将无法编译,暗示Widget必须在此时不完整。

这种行为的正确解释是什么?

编辑:一些答案(现已删除)指向未定义的行为但我希望在scoped_ptr的析构函数中使用checked_delete导致编译失败。 FWIW,我使用的是Boost 1.55。

1 个答案:

答案 0 :(得分:4)

  

5.3.5删除[expr.delete]

     

5如果被删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为是未定义的。

因此,你肯定会期望它是UB,因为Widget::~Widget()是非平凡的,并且你会期望安全防范在提升中出错。

现在,让我们进一步挖掘:

  

2.2翻译阶段[lex.phases]

     

8翻译的翻译单元和实例化单元组合如下: [注意:...] 检查每个翻译的翻译单元以生成所需实例化的列表。 [注意:这可能包括已明确请求的实例化(14.7.2)。 -end note] 找到所需模板的定义。实现定义是否需要包含这些定义的翻译单元的来源。 [注意:实现可以将足够的信息编码到翻译的翻译单元中,以确保此处不需要源。 -end note] 执行所有必需的实例化以生成实例化单元。 [注意:这些与翻译的翻译单元类似,但不包含对未实例化模板的引用,也没有模板定义。 -end note] 如果任何实例化失败,程序就会格式不正确。

您通过翻译阶段保存:
翻译翻译单元,然后实例化模板......