Могу ли я абстрагироваться от конкретных деталей о шаблонных классах?

Я работаю над созданием фабричной библиотеки (похожей на https://github.com/google/guice которая доступна для Java) в C++11, чтобы ознакомиться с программированием шаблонов, а также создать полезный инструмент для уменьшения зависимости, Идея заключалась в том, что фабрика будет абстрагироваться от деталей создания и уничтожения объекта и скрывать детали реализации. В идеале я хотел бы иметь что-то похожее на:

InterfaceClass
{
    public:
    virtual void doSomething () = 0;
    virtual ~InterfaceClass () {};
}

// Might need custom deleter depending on how the class was allocated
// (might come from a pool, etc)
ImplementationClass : public InterfaceClass
{
    public:
    // Some (possibly) complicated constructor.
    ImplementationClass(Dependency one, Other dependency) {}

    virtual void doSomething ()
    {
        // Implementation
    }

    virtual ~ImplementationClass ()
    {

    }
}

В идеале я бы хотел, чтобы конечный пользователь библиотеки мог (или что-то подобное):

std::unique_ptr<InterfaceClass> object = factory<InterfaceClass>();

Это прекрасно работает, если все классы используют средство удаления по умолчанию, но в случае пользовательских средств удаления тип unique_ptr изменяется с:

std::unique_ptr<I> 

чтобы:

std::unique_ptr<I, deleter> 

- и, насколько я могу судить, эти типы не совместимы.

Есть ли способ, которым я могу определить своего рода "уникальный указатель" более высокого уровня, который не заботится об удалителе в его сигнатуре типа? Другие возможные обходные пути для сохранения API независимым от создания / удаления объекта?

Спасибо!

2 ответа

Решение

Использование std::function иметь общий тип удаленного типа.

Идеальная ссылка

#include <iostream>
#include <memory>
#include <functional>

template<typename T>
using TypeErasedUPtr = std::unique_ptr<T, std::function<void(T*)>>;

int main() 
{
    TypeErasedUPtr<int> p1{new int(5), [](int* x){ delete x; }};
    TypeErasedUPtr<int> p2{someAllocator<int>(5), [](int* x){ someDeallocator(x); }};

    // `p1` and `p2` have the same type, `TypeErasedUPtr<int>`.

    return 0;
}

Это работает, потому что std::unique_ptr<T, TDeleter> принимает любой вызываемый TDeleter тип, который может быть вызван с T* параметр.

std::function<void(T*)> удовлетворяет этим требованиям, а также полиморфно оборачивает любую функцию с этой сигнатурой во время выполнения (платя небольшую накладную цену во время выполнения).

Вы всегда можете обернуть тип, который требует пользовательского удаления. На самом деле вы должны сделать это, так как пользовательские средства удаления - это детали реализации, которые вы в любом случае не хотели бы показывать общественности.

Другие вопросы по тегам