Does the GotW #101 "solution" actually solve anything?
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 for pimpl<widget::impl>::pimpl()
and pimpl<widget::impl>::~pimpl()
. When I add template 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 the pimpl
impl. At least with #101 the boiler plate you need is straightforward.
I think the confusion is this: the pimpl wrapper may be a template, the widget class isn't:
demo.h
#include "pimpl_h.h"
// in header file
class widget {
public:
widget();
~widget();
private:
class impl;
pimpl<impl> pimpl_;
};
demo.cpp
#include "demo.h"
#include "pimpl_impl.h"
// in implementation file
class widget::impl {
// :::
};
widget::widget() : pimpl_() { }
widget::~widget() { } // or =default
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 the impl
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:
- use a template to guard against walking into pitfalls unnecessarily
- you get the variadic, perfect forwarding constructors with C++0x/C++11, no need to dream that templated constructor template with rvalue-reffed variadic argument list forwarding the argument pack to the wrapped classes' constructor perfectly etc. etc.
In short: DRY and convenience.
1 to quote (emphasis mine):
- First, the code is simpler because it eliminates some pieces of boilerplate: In the hand-rolled version, you also have to declare the constructor and write its body in the implementation file and explicitly allocate the impl object. You also have to remember to declare the destructor and write its body in the implementation file, for obscure language reasons explained in #100.
- Second, the code is more robust: In the hand-rolled version, if you forget to write the out-of-line destructor, the Pimpl’d class will compile in isolation and appear to be in a check-in-able state, but will fail to compile when used by a caller that tries to destroy an object and encounters a helpful “cannot generate destructor because impl is, uh, you know, incomplete” error that leaves the author of the calling code scratching his head as he walks over to your office to ream you out for checking in something broken.