First read Herb's Sutters GotW posts concerning pimpl in C++11:
I'm having some trouble understanding the solution proposed in GotW #101. As far as I can understand, all the problems laboriously solved in GotW #100 are back with a vengeance:
The
pimpl
members are out-of-line templates, and the definitions are not visible at the point of use (inclass widget
's class definition and implicitly generated special member functions ofwidget
). There aren't any explicit instantiations either. This will cause unresolved external errors during linking.widget::impl
is still incomplete at the point wherepimpl<widget::impl>::~pimpl()
isinstantiateddefined (I don't think it actually IS instantiated at all, just referenced). Sostd::unique_ptr<widget::impl>::~unique_ptr()
callsdelete
on a pointer to incomplete type, which produces undefined behavior ifwidget::impl
has a non-trivial destructor.
Please explain what forces the compiler to generate the special members in a context where widget::impl
is complete. Because I can't see how this works.
If GotW #101 still requires explicit definition of widget::~widget()
in the implementation file, where widget::impl
is complete, then please explain the "More Robust" comment (which @sehe quoted in his answer).
I'm looking at the core claim of GotW #101 that the wrapper "eliminates some pieces of boilerplate", which seems to me (based on the remainder of the paragraph) to mean the widget::~widget()
declaration and definition. So please don't rely on that in your answer, in GotW #101, that's gone!
Herb, if you stop by, please let me know if it would be ok to cut+paste the solution code here for reference.
I think the confusion is this: the pimpl wrapper may be a template, the widget class isn't:
demo.h
demo.cpp
As you can see, nobody will ever see a 'templated' constructor for the widget class. There will be only one definition of it, and no need for 'explicit instantiation'.
Conversely, the
~pimpl<>
destructor is only ever instantiated from the single point of definition of the~widget()
destructor. At that point theimpl
class is complete, by definition.There are no linkage errors and no ODR/UB violations.
Bonus/extra benefits
As Herb himself aptly explains in his post (Why is this an improvement over the hand-rolled Pimpl Idiom?1), there are many more advantages to using this pimpl wrapper, that stem from reusing the implementation guts:
In short: DRY and convenience.
1 to quote (emphasis mine):
You are correct; the example seems to be missing an explicit template instantiation. When I try to run the example with a constructor and destructor for
widget::impl
on MSVC 2010 SP1, I get a linker error forpimpl<widget::impl>::pimpl()
andpimpl<widget::impl>::~pimpl()
. When I addtemplate class pimpl<widget::impl>;
, it links fine.In other words, GotW #101 eliminates all boilerplate from GotW #100, but you need to add an explicit instantiation of the
pimpl<...>
template with the implementation of thepimpl
impl. At least with #101 the boiler plate you need is straightforward.