pimpl-idiom in template; which smart pointer?
Asked Answered
I

3

6

I usually use a boost::scoped_ptr for pimpl's (for one reason because then I don't get surprises if I forget to deal with the copy constructor)

With templates however I can't just put the destructor in the cpp file where the impl is fully defined in order to fulfill the requirements of scoped_ptr's destructor. It does work anyway but I'm not sure if its garanteed to work or just by chance. Is there some 'best practice' or standard? Is scoped_ptr the best smart pointer for pimpls in non-copyable classes?

template <class T> class C {
public:
    C(){}
    ~C(){}
private:
    boost::scoped_ptr<T> pimpl_;
};
Intenerate answered 5/12, 2011 at 14:5 Comment(6)
This sort of PIMPL implementation doesn't make any sense because in order to instantiate C template you have to know about type T. PIMPL, on the other hand, fully hides an equivalent of T from the user.Mogilev
@VladLazarenko Hmm, I thought boost::scoped_ptr works as well on predeclared classes. In this case it depends if T is defined or predeclared. The instanciation of this scoped_ptr would be hidden in the implementation (pimpl_(new T()).Demello
@DavidFeurle: Not really, in order for this template to work, the size of T, as well as its interface, must be exposed, because "clients" need to instantiate the template. For example, where do you call new T()? You cannot hide that in "cpp" file because it must be in template. So it is not PIMPL.Mogilev
@Vlad Lazarenko good point. I see where I made my mistake and why it worksIntenerate
So it looks like in this case since I have to define T anyway I sould just make it a private member to save cost.Intenerate
Pimpl is used to hide the details of a given class. Which class details do you want to hide here?Nubile
F
1

boost::shared_ptr doesn't require a complete definition other than at the point of instantiation—in the constructor, in the case of a pimpl. boost::shared_ptr is not appropriate for the pimpl idiom, however, since it gives very unexpected semantics (reference semantics for assignment or copy); if you really want the added complexity of a smart pointer, boost::scoped_ptr would be more appropirate (but it does require a full definition at the point its destructor is instantiated).

With regards to templates, it makes no sense to use the pimpl idiom for the implementation details from the header. In the absense of export, all of the implementation details of a class template must be included everywhere the template is used, so the motivation behind the pimpl idiom ceases to exist.

Fragrance answered 5/12, 2011 at 14:40 Comment(0)
A
13

It just happens that Herb Sutter started to write his GotWs again after a long time. One of the first new ones are related to "Compilation Firewalls".

You might want to take a look at:

GotW #100: Compilation Firewalls (Difficulty: 6/10)

and

GotW #101: Compilation Firewalls, Part 2 (Difficulty: 8/10)

Abductor answered 5/12, 2011 at 14:36 Comment(2)
Cool I love his books. He seems to have answered my question about what pointer type to use by using unique_ptr.Intenerate
Looks like Herb's bought into the more complexity is better philosophy. All of his standard-based solutions introduce extra complexity for no real gain.Fragrance
I
2

Two years later I understand the situation much better, in the interest of keeping stack overflow answers relevant and up to date here is how I would answer the question today.

The premise of my original question is somewhat flawed. The reason to use the pimpl-idiom is to hide implementation details from the compiler. This is done by storing the implementation via an opaque pointer (pointer to a declared but not defined data type). This can greatly reduce the amount of headers needed by other compilation units which interact with the class and thus speed up compile time. In the case of the template in my question it is required that the type T be fully known at the point of instantiation which in practice requires the the impl's type be fully defined wherever C<ImplType> is used making this clearly not an example of the pimpl-idiom in the classic sense of the term.

There are other reasons to hold a classes data via a private pointer, for example it allows for easy implementation of no-throw move and swap and is also good if your class needs to fulfill a strong exception guarantee (see copy and swap idiom What is the copy-and-swap idiom?). On the other hand it adds a layer of indirection (often resulting in a cache miss) on every access to the impl and a heap allocation/deallocation upon creation and destruction of the impl. These can be substantial performance penalties, therefore this solution should not be considered a silver bullet.

If you can use C++11 then std::unique_ptr should be used instead of boost::scoped_ptr.

Intenerate answered 11/6, 2014 at 12:30 Comment(0)
F
1

boost::shared_ptr doesn't require a complete definition other than at the point of instantiation—in the constructor, in the case of a pimpl. boost::shared_ptr is not appropriate for the pimpl idiom, however, since it gives very unexpected semantics (reference semantics for assignment or copy); if you really want the added complexity of a smart pointer, boost::scoped_ptr would be more appropirate (but it does require a full definition at the point its destructor is instantiated).

With regards to templates, it makes no sense to use the pimpl idiom for the implementation details from the header. In the absense of export, all of the implementation details of a class template must be included everywhere the template is used, so the motivation behind the pimpl idiom ceases to exist.

Fragrance answered 5/12, 2011 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.