C4150:删除指向不完整类型和PIMPL惯用语的指针

时间:2014-01-27 20:31:34

标签: c++ pimpl-idiom

有没有办法在C ++中用模板类正确实现PIMPL习惯用法?

例如,我有以下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
}

我明显在.cpp中有LoggerStreamImpl定义的析构函数,那么是什么给出了?

谢谢。

2 个答案:

答案 0 :(得分:1)

当我存储一个我不希望完全可见的类型的std :: unique_ptr时,比如pimpl,我使用自定义删除器,其中operator()在cpp中定义,完整类可见。

它可以实现这一目的,并且使用链接时间优化,如果编译器认为它是相关的,即使引入的函数调用间接也可以进行优化。

答案 1 :(得分:0)

任何包含logger.h的文件都会尝试为自己编译模板。因此,假设您有一个包含main.cpp(或其他入口点)的int main()文件,该文件包含logger.h。 Main.cpp将看到logger.h,尝试解析它,在解析它的过程中,它将尝试为PrivateImplementation编译T = LoggerStreamImpl模板的版本。如果您的编译器符合C ++ 11标准,并且允许您告诉它PrivateImplementation是在外部定义的,那么您可以使用它。