Re: Deleting a pointer to an incomplete class
* Kai-Uwe Bux:
Alf P. Steinbach wrote:
You can "delete" a pointer to incomplete type. But IIRC you have
Undefined Behavior if that type then defines a non-trivial destructor.
And that was the problem with Herb Sutter's original GOTW on the PIMPL
idiom, where he employed std::auto_ptr for the implementation object.
Thanks! You are refering to [5.3.4/5], right?
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.
If I had looketh in the Holy Standard I guess that would be it, yes.
Now, the question becomes: which line in the program is the point of
deletion? I would think, in the snippet I posted, it's the line in the
destructor of Y, i.e., the following has UB:
#include <iostream>
#include <ostream>
struct X;
struct Y {
X * x_ptr;
Y ( X * ptr = 0 )
: x_ptr ( ptr )
{}
~Y ( void ) {
Uh, C-ism, please don't do that Kai! Hurts me... ;-)
delete x_ptr;
}
};
struct X {
~X ( void ) {
std::cout << "crash me\n";
}
};
int main ( void ) {
Y y ( new X );
}
Thus, it makes perfect sense that it does not print anything with g++ :-)
Yes.
However, I was more interested in templates anyway (posted an oversimplified
piece of code). So what about these:
A (typedef)
===========
#include <iostream>
#include <ostream>
template < typename X>
struct Y {
X * x_ptr;
Y ( X * ptr = 0 )
: x_ptr ( ptr )
{}
~Y ( void ) {
delete x_ptr;
}
};
struct X;
typedef Y<X> YX;
struct X {
~X ( void ) {
std::cout << "crash me\n";
}
};
int main ( void ) {
YX yx ( new X );
}
Hm. Hm. I /think/ the template isn't instantiated until 'main' (i.e.,
the typedef doesn't do anything other than introduce a name), where the
definition of X is known, and so it should be OK.
However, there's also the practical matter of compilers that don't
implement two-phase template instantiation.
B (inheritance)
===============
#include <iostream>
#include <ostream>
template < typename X>
struct Y {
X * x_ptr;
Y ( X * ptr = 0 )
: x_ptr ( ptr )
{}
~Y ( void ) {
delete x_ptr;
}
};
struct X;
struct YX : public Y<X> {
I think that right here Y<X> is instantiated, with still incomplete type
X, and thus Undefined Behavior.
YX ( X * ptr = 0 )
: Y<X>( ptr )
{}
};
>
struct X {
~X ( void ) {
std::cout << "crash me\n";
}
};
int main ( void ) {
YX yx ( new X );
}
C (position of function definition)
===================================
#include <iostream>
#include <ostream>
template < typename X>
struct Y {
X * x_ptr;
Y ( X * ptr = 0 )
: x_ptr ( ptr )
{}
~Y ( void ) {
delete x_ptr;
}
};
struct X;
void f ( X * ptr = 0 ) {
Y<X> y ( ptr );
}
Instantiation with incomplete type X, Undefined Behavior.
struct X {
~X ( void ) {
std::cout << "crash me\n";
}
};
int main ( void ) {
f ( new X );
}
In my experiments with g++, all these programs print "crash me".
Hm, B and C "should", as far as I understand it, exhibit UB, which of
course means they could by chance print "crash me", but in practical
terms a compiler-generated empty X destructor should be invoked.
Checking...
I get the same result as your g++-testing by using MSVC 7.1. However,
using MingW g++ 3.4.4 B and C do not print anything (and warnings are
issued for the destructor calls), while A does print "crash me" (and no
warnings). This is consistent with g++ generally being much more
standard-conforming wrt. templates than MSVC.
Where is
the "point of deletion" in these programs when template instantiation
issues enter the picture? Which of the above have UB?
See above. I think I got it right; at least, testing above indicates
that. So, A is OK (A OK ;-)), while B and C are UB.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?