【问题标题】:C4150: Deletion of pointer to incomplete type and PIMPL idiomC4150:删除指向不完整类型和 PIMPL 习语的指针
【发布时间】:2014-01-27 20:31:34
【问题描述】:

有什么方法可以在 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 定义的析构函数,那么给出了什么?

谢谢。

【问题讨论】:

    标签: c++ pimpl-idiom


    【解决方案1】:

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

    它可以解决问题,并且通过链接时间优化,如果编译器认为它是相关的,即使引入的函数调用间接也可以优化。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 我正在使用 C++11 编译器(但合规性并不是它的强项)...我如何告诉它“PrivateImplementation 是在外部定义的”?
      • logger.h 中类定义之前的extern template class PrivateImplementation&lt;LoggerStreamImpl&gt;; 行应该告诉编译器该类已在别处实例化并且不实例化它。我相信您需要在 logger.cpp 中包含行 template class PrivateImplementation&lt;LoggerStreamImpl&gt;; 以强制该文件实际实例化模板,否则它不会在任何地方实例化。让我知道这是否有效,因为我还没有使用外部模板。
      • 没有骰子,将extern template class PrivateImplementation&lt;LoggerStream::LoggerStreamImpl&gt;; 添加到 Logger.h 会导致编译器错误:'extern template' 不能遵循类“ophRuntime::PrivateImplementation<: loggerstream::loggerstreamimpl>"... 是的,我把它放在类声明之上。 :(
      • 您是否将template class PrivateImplementation&lt;LoggerStreamImpl&gt;; 放在代码中的某个位置?如果有,在哪里?
      • 是的,它在包含下方。感谢您的帮助,但最终现在我将使用 dtor 中的 delete 以“老式”方式执行此操作。有一点(尤其是使用 C++)必须放弃并提交:D
      猜你喜欢
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      • 2021-12-31
      • 2012-08-15
      相关资源
      最近更新 更多