Re: Smart-pointer pimpl and compiler-generated destructors.
On 25 Apr., 18:53, rwf_20 <rfr...@gmail.com> wrote:
I'd like to use pimpl to reduce dependencies as follows:
// myClass.h
#include <memory>
class myClass {
public:
myClass();
private:
struct myStruct;
std::auto_ptr<struct myStruct> m_pimpl;
Strictly speaking, this member definition brings you into
undefined behavior, although most (if not all)
implementations of auto_ptr will just "work". Reason
for this maybe surprising outcome is that
[lib.res.on.functions]/2 applies for auto_ptr, because
auto_ptr does not give an explicit guarantee (as upcoming
shared_ptr does), which invalidates the following
assertion:
"In particular, the effects are undefined in the following
cases: [..]
? if an incomplete type (3.9) is used as a template argument
when instantiating a template component."
// myClass.cpp
#include "myClass.h"
struct myClass::myStruct {
// ...details
};
myClass::myClass() : m_pimpl(std::auto_ptr<myStruct>(new myStruct()))
{ }
This compiles just fine, until I try to use myClass elsewhere. At
this point, I get warnings like:
"deletion of pointer to incomplete type 'myStruct'; no destructor
called"
You are lucky that your compiler warns you, because you
are in undefined-behavior land (not only because of above
mentioned point), but read on...
I guess I understand why this happens, and it's fixed by declaring a
destructor for myClass and defining it in the .cpp file. It just irks
me that I need to define a blank destructor (the same thing the
compiler should generate) to make this work.
Yes, this is the right choice in the general case, v.i.
Googling this issue, I
findhttp://www.gotw.ca/publications/using_auto_ptr_effectively.htm,
in which Mr. Sutter demonstrates this same code and adds the comment:
"In fact, if there's no other reason for explicitly writing a
destructor, we don't need to bother with a custom destructor at all
any more."
I'm seeing that this is not the case. Unless (likely) I am doing
something wrong. Thoughts?
Although the article is fine, the above claim does not hold
in general. Even with auto_ptr (and ignoring for the moment
the additional problem that auto_ptr does unfortunately not
give any grant for incomplete types) you need to provide the
destructor explicitly, *after* the definition of the class
has been seen, *if* the d'tor of myClass::myStruct is non-trivial.
This belongs to the rule [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."
Why does this rule apply here? Because the standard says in
[class.dtor]/3:
"If a class has no user-declared destructor, a destructor is
declared implicitly. An implicitly-declared destructor is an
inline public member of its class.[..]"
This has the effect that the d'tor of myClass will be defined
"in" the header myClass.h, where myClass::myStruct is still
incomplete, therefore [expr.delete]/5 comes into the game.
Since we do not see the complete definition of C::CImpl in
Herb Sutter's article, we cannot say whether this is required
here or not, but I would recommend to add at least a note
regarding this and I would *never* trust on the assumption.
It's so easy to overlook this, if today myClass::myStruct has
trivial d'tor, but tomorrow we want to lift this constraint.
Still better, define the d'tor of myClass immediately in the
..cpp after the incomplete type is completed.
In C++0x you can take advantage of an explicitly defaulted,
but *non-trivial* d'tor like this:
//myClass.h:
class myClass {
public:
myClass();
~myClass(); // only declare
....// same as your definition
};
//myClass.cpp
#include "myClass.h"
struct myClass::myStruct {
// ...details
};
// user-provided, explicitly defaulted:
myClass::~myClass() = default;
Of-course this is not really shorter than simply
writing
myClass::~myClass(){}
but it does better explain it's intend.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]