Рассмотрим следующий фрагмент кода. Деструктор boost :: scoped_ptr вызывается в конце основной функции. Деструктор использует boost :: checked_delete для освобождения инкапсулированного указателя виджета.
#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. Обратите внимание на закомментированный оператор с sizeof(Widget)
Выражение в основной функции. Если я раскомментирую его, он не будет компилироваться, подразумевая, что Widget
должно быть неполным в этой точке.
Каково правильное объяснение этого поведения?
РЕДАКТИРОВАТЬ: Некоторые ответы (в настоящее время удалены) указали на неопределенное поведение, но я ожидал бы использование check_delete в scoped_ptr
деструктор, чтобы вызвать сбой компиляции. FWIW, я использую Boost 1.55.
5.3.5 Удалить
[expr.delete]
5 Если удаляемый объект имеет неполный тип класса в точке удаления, а полный класс имеет нетривиальный деструктор или функцию освобождения, поведение не определено.
Таким образом, вы, конечно, ожидаете, что это будет UB, так как Widget::~Widget()
является нетривиальным, и вы ожидаете, что безопасный буст в форсировке выдает ошибку.
Теперь давайте копать выше:
2.2 Фазы перевода
[lex.phases]
8 Переводимые единицы перевода и экземпляры создаются следующим образом: [ Заметка: … ] Каждая переведенная единица перевода проверяется для составления списка необходимых экземпляров. [Примечание: это может включать в себя экземпляры, которые были явно запрошены (14.7.2). —Конечная записка] Определения необходимых шаблонов находятся. Это зависит от реализации, должен ли быть доступен источник блоков перевода, содержащих эти определения. [Примечание: реализация может закодировать достаточную информацию в переведенную единицу перевода, чтобы гарантировать, что источник здесь не требуется. —Конечная записка] Все необходимые экземпляры выполняются для создания единиц реализации. [Примечание: они похожи на переведенные единицы перевода, но не содержат ссылок на необоснованные шаблоны и определения шаблонов. —Конечная записка] Программа плохо сформирована, если какая-либо инстанциация не удалась.
Вы спасены этапы перевода:
Переведите переводческий блок, затем создавать шаблоны …