4

First, some context: I'm trying to use the Pimpl idiom in the way Herb Sutter presented it in the solution to his GotW #101. This would look like this in the header file:

#include "pimpl_h.h"
class widget {
    class impl;
    pimpl<impl> m;
    // ...
};

The implementation would look like this:

#include "pimpl_impl.h"
class widget::impl {
    // ...
};

The problem I am trying to solve appears when a class using this technique uses another Pimpl class for their own implementation. It includes the "pimpl_impl.h", so the compiler (in my case VC++ 2013) gains knowledge of the concrete template for pimpl <impl> of the other class and tries to implicitly instantiate it, which of course results in compilation errors as it does not know about the implementation of that class.

To solve this, I have used the new "extern template" feature of C++ 11 in the header:

#include "pimpl_h.h"
class widget {
    class impl;
    pimpl<impl> m;
    // ...
};
extern template pimpl<widget::impl>;

This should guarantee that only the explicit instantiation I have in the compilation unit providing the implementation of widget::impl leads to an actual instantiation. This compiles without a problem, but IntelliSense shows me an error:

Error: 'extern template' cannot follow explicit instantiation of class "pimpl<widget::impl>"

As "extern template" cannot be used inside of a class declaration, I cannot write

#include "pimpl_h.h"
class widget {
    class impl;
    extern template pimpl<impl>;
    pimpl<impl> m;
    // ...
};

and I can't think of any other way. My question is:

Is IntelliSense wrong and the compiler right in accepting my code? Or is it just coincidence that VC++ compiles this and it isn't valid C++?

If my solution is not valid C++, what alternatives do I have?

jblume
  • 390
  • 1
  • 6

1 Answers1

2

I think I figured this out myself. In ยง14.7.2.11 the standard says

If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration.

which is probably what IntelliSense refers to. And this is where I noticed that the error message says "...cannot follow explicit instantiation...". There is obviously no explicit instantiation anywhere, just an implicit instantiation inside the class definition of widget. So I assume that this is a bug inside IntelliSense. Inside the same paragraph, the standard says

An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation (14.7.1) in the translation unit shall be the subject of an explicit instantiation definition some- where in the program; otherwise the program is ill-formed, no diagnostic required.

which does not require any specific order of (potential) implicit instantiation and explicit instantiation declaration.

At this point I also realized that my solution is actually overkill for my original problem. I did not want to prevent the implicit instantiation of both the template class declaration and definition of pimpl<impl>, but only the template class definition inside pimpl_impl.h. The extern template pimpl<impl> suppressed both, which solved my problem but does more than necessary. The solution is to declare the actual members of pimpl<impl> with extern template and explicitly instantiate them later.

jblume
  • 390
  • 1
  • 6