I wrote this article and got some comments on it that confused me.
It basically boils down to my having seen T2
used only as a template parameter and mistakenly jumped to the conclusion that I could therefore take the opportunity of forward declaration:
struct T2;
struct T1
{
std::auto_ptr<T2> obj;
};
This invokes UB if I don't go on to define T2
somewhere in the same TU, because std::auto_ptr<T2>
calls delete
on its internal T2*
, and calling delete
on an pointer to an object of an incomplete type whose complete type has a non-trivial destructor is undefined:
[C++11: 5.3.5/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.
The GCC toolchain I happened to be using — v4.3.3 (Sourcery G++ Lite 2009q1-203) — was kind enough to let me know with a note:
note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
though it seems difficult to get this diagnostic in other GCC versions.
My gripe was that it would be a lot easier to spot a bug like this if delete
ing a pointer to an instance of an incomplete type were ill-formed rather than UB, but that seems like an intractible problem for an implementation to solve, so I understand why it's UB.
But then I'm told that, if I were to use std::unique_ptr<T2>
instead, this would be safe and compliant.
n3035 allegedly says at 20.9.10.2:
The template parameter
T
ofunique_ptr
may be an incomplete type.
All I can find in C++11 proper is:
[C++11: 20.7.1.1.1]:
/1 The class template
default_delete
serves as the default deleter (destruction policy) for the class templateunique_ptr
./2 The template parameter
T
ofdefault_delete
may be an incomplete type.
But, default_delete
's operator()
does require a complete type:
[C++11: 20.7.1.1.2/4]:
IfT
is an incomplete type, the program is ill-formed.
I suppose my question is this:
Are the commenters on my article correct in saying that a translation unit consisting of only the following code is well-formed and well-defined? Or are they wrong?
struct T2;
struct T1
{
std::unique_ptr<T2> obj;
};
If they are correct, how is a compiler expected to implement this, given that there are good reasons for it being UB, at least when an std::auto_ptr
is used?
static_assert( sizeof(T), "" )
). So yes,std::unique_ptr<T>
is safer thanstd::auto_ptr<T>
. (But do note that's due to the use of the defaultstd::default_delete<T>
deleter.) – Roobbie