есть ли способ правильно реализовать идиому PIMPL с классом шаблона в C++?
Например, у меня есть следующий класс PIMPL:
template <typename T> struct PrivateImplementation {
PrivateImplementation<T>(T* impl) : implementation(impl) {
}
~PrivateImplementation<T>() {
if (implementation != nullptr) delete implementation;
}
PrivateImplementation<T>(const PrivateImplementation<T>& c) = delete;
void operator=(const PrivateImplementation<T>& a) = delete;
PrivateImplementation<T>(PrivateImplementation<T>&& m) {
implementation = m.implementation;
m.implementation = nullptr;
}
T* operator->() const {
return implementation;
}
private:
T* implementation;
};
Однако, когда я использую его, как показано ниже, компилятор жалуется (предупреждение), что я удаляю неполный тип:
logger.h
class LoggerStream {
public:
LoggerStream(std::wstring componentName);
~LoggerStream();
LoggerStream(const LoggerStream&) = delete;
void operator=(const LoggerStream&) = delete;
LoggerStream(LoggerStream&&);
void Write(std::wstring message, const char* __function__, const char* __file__, int __line__) const;
void Write(std::wstring message) const;
private:
struct LoggerStreamImpl;
PrivateImplementation<LoggerStreamImpl> impl;
};
Logger.cpp
struct LoggerStream::LoggerStreamImpl {
LoggerStreamImpl(std::wstring componentName) : componentName(componentName) { }
~LoggerStreamImpl() = default;
LoggerStreamImpl(const LoggerStreamImpl&) = delete;
void operator=(const LoggerStreamImpl&) = delete;
LoggerStreamImpl(LoggerStreamImpl&&);
const std::wstring componentName;
};
LoggerStream::LoggerStream(std::wstring componentName)
: impl(new LoggerStreamImpl { componentName }) { }
LoggerStream::~LoggerStream() { }
void LoggerStream::Write(std::wstring message, const char* __function__, const char* __file__, int __line__) const {
// Some implementation
}
void LoggerStream::Write(std::wstring message) const {
// Some implementation
}
У меня явно есть определенный деструктор для LoggerStreamImpl
прямо там.cpp, так что даёт?
Спасибо.
Любой файл, который включает logger.h, пытается скомпилировать шаблон для себя. Итак, скажем, у вас есть файл main.cpp
который содержит вашу int main()
(или другую точку входа), и этот файл включает logger.h. Main.cpp увидит logger.h, попытается разобрать его, и во время процесса синтаксического анализа попытается скомпилировать версию шаблона PrivateImplementation
для T = LoggerStreamImpl
. Вы можете уйти от этого, если ваш компилятор совместим с С++ 11 и позволяет вам сказать, что PrivateImplementation определяется извне.
Когда я храню std :: unique_ptr с типом i, я не хочу полностью видимого, например pimpl, я использую настраиваемый делектор, где оператор() определен в cpp с полным видимым классом.
Это делает трюк, и с Оптимизацией времени связи, даже введенная функция вызова функции может быть оптимизирована, если компилятор считает, что это уместно.
extern template class PrivateImplementation<LoggerStreamImpl>;
в logger.h непосредственно перед определением класса следует сообщить компилятору, что этот класс создан в другом месте и не создавать его. Я считаю, что вам нужно будет включитьtemplate class PrivateImplementation<LoggerStreamImpl>;
линииtemplate class PrivateImplementation<LoggerStreamImpl>;
в logger.cpp, чтобы заставить этот файл на самом деле создать экземпляр шаблона, иначе он нигде не будет создан. Дайте мне знать, если это работает, поскольку я еще не играл с внешними шаблонами.