Consider the following code snippet. The destructor of boost::scoped_ptr is invoked at the end of the main function. The destructor uses boost::checked_delete to deallocate the encapsulated Widget pointer.
#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;
}
I expected this code to fail to compile as the class Widget is incomplete at the point the destructor of scoped_ptr<Widget>
is invoked. However this compiles cleanly on g++ 4.8 and Visual Studio 2010. Note the commented statement with the sizeof(Widget)
expression in the main function. If I uncomment it, it will fail to compile implying that Widget
must be incomplete at that point.
What is the correct explanation for this behavior?
EDIT: Some answers (now deleted) pointed to undefined behavior but I would have expected the use of checked_delete in scoped_ptr
's destructor to cause a compilation failure. FWIW, I'm using Boost 1.55.
5.3.5 Delete
[expr.delete]
5 If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.
Thus, you certainly would expect it to be UB, as Widget::~Widget()
is non-trivial, and you would expect the safe-guard in boost to error out.
Now, let's dig higher:
2.2 Phases of translation
[lex.phases]
8 Translated translation units and instantiation units are combined as follows: [ Note: ... ] Each translated translation unit is examined to produce a list of required instantiations. [ Note: This may include instantiations which have been explicitly requested (14.7.2). —end note ] The definitions of the required templates are located. It is implementation-defined whether the source of the translation units containing these definitions is required to be available. [ Note: An implementation could encode sufficient information into the translated translation unit so as to ensure the source is not required here. —end note ] All the required instantiations are performed to produce instantiation units. [ Note: These are similar to translated translation units, but contain no references to uninstantiated templates and no template definitions. —end note ] The program is ill-formed if any instantiation fails.
You are saved by phases of translation:
Translate the translation unit, then instantiate templates...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With