Search code examples
c++templatesunique-ptrpimpl-idiom

using pimpl pattern in templated class. How to allocate a unique_ptr


My colleague insists our project should be able to use a template like this:

template <typename T, typename B>
class Foo
{
public:
    Foo(T t, B b);

    // some more functions

private:
    struct pimpl;
    std::unique_ptr<pimpl> m_impl;
};

I'm not sure if they're just trying to test me or what, but I can't figure what's the proper and correct way we should have to allocate m_impl. They insist it should be possible to write the Foo::Foo(T t, B b) constructor with this.

How should I be able to allocate it:

template<typename T, typename B>
Foo<T, Op>::Foo(T t, B b)
{
    m_impl = // ... what goes here ?
}

To me, it seems anything I do will be a hack since the class is private and not defined. I tried:

m_impl = std::unique_ptr<pimpl>(reinterpret_cast<pimpl*>(new T));

But this results in

error: implicit instantiation of undefined member 'accumulator<int, std::plus>::pimpl'

from

in instantiation of member function 'std::unique_ptr<Foo<int, std::plus>::pimpl>::operator=' requested here pimpl_ = std::unique_ptr(reinterpret_cast<pimpl*>(new T));

Is there an elegant way to go with such a pimpl in a templated class ?


Solution

  • The key here is to separate the declaration of the template from the implementation. Typical implementations will separate the implementation file from the template into a separate header file. Note, because these are all templates, it still must be in a header file, but it can be in a second file which is included from the first.

    So, in this separate header file, you would first define the pimpl struct, and then define all of the methods on the original template:

    template<typename T, typename B>
    struct Foo<T,B>::pimpl {
        // Private implemenation 
    };
    
    
    template<typename T, typename B>
    Foo<T,B>::Foo(T t, B b) {
        m_impl = std::make_unique<pimpl>();
    }
    

    Note, unlike non-template pimpl use cases, you must still eventually provide the pimpl template to the user in the header file, so its a matter of moving it to a separate header, not fully hiding it.

    See it compiling at https://godbolt.org/z/jhYfrMszd