You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在类中自动导出std::unique_ptr模板实例(VS2015+C++11)

How to Avoid Exporting Each std::unique_ptr Specialization for a PImpl DLL Template

Great question! When working with the PImpl idiom in a DLL using Visual Studio 2015 (C++11+), manually exporting every std::unique_ptr specialization for your implementation classes is tedious and error-prone. Here are two clean approaches to automate this, assuming your library users are on the exact same STL and compiler version as you:

Approach 1: Explicitly Instantiate Your PImpl Template in the DLL

The key here is to keep your PImpl template unexported in the header, then explicitly instantiate and export only the specific PImpl specializations you need inside your DLL's source files. This way, std::unique_ptr's specialization is resolved internally in the DLL, no need to export it separately.

Step 1: Adjust the Header File (PImpl.h)

Remove the __declspec(dllexport) from the PImpl template and omit the invalid extern template declaration for std::unique_ptr. Instead, declare the template's public/protected interface and your exported library class:

// PImpl.h
#include <memory>

template<typename PImpl_T> 
class PImpl {
protected:
    std::unique_ptr<PImpl_T> m_pImpl;

    // Declare constructor/destructor (define them in .cpp)
    PImpl();
    ~PImpl();

    // Add other helper methods if needed (e.g., accessors)
};

// Export your library class, inheriting from the PImpl template
class __declspec(dllexport) MyClass : private PImpl<struct MyClassImpl> {
public:
    // MyClass public interface here
    MyClass();
    void doSomething();
};

Note: MyClassImpl is only declared here (as an incomplete type) — its definition lives exclusively in your DLL's .cpp files, keeping it hidden from users.

Step 2: Implement and Instantiate in the DLL Source (PImpl.cpp)

In your DLL's implementation file, include the definition of MyClassImpl, implement the PImpl template methods, and explicitly instantiate the PImpl<MyClassImpl> specialization with export:

// PImpl.cpp
#include "PImpl.h"
#include "MyClassImpl.h" // Contains the full definition of MyClassImpl

// Implement PImpl template methods
template<typename PImpl_T>
PImpl<PImpl_T>::PImpl() : m_pImpl(std::make_unique<PImpl_T>()) {}

template<typename PImpl_T>
PImpl<PImpl_T>::~PImpl() = default; // Safe here since PImpl_T is complete

// Explicitly instantiate and export the PImpl specialization for MyClassImpl
template class __declspec(dllexport) PImpl<MyClassImpl>;

// Implement MyClass methods
MyClass::MyClass() = default;

void MyClass::doSomething() {
    m_pImpl->internalMethod(); // Access the implementation
}

This works because:

  • The std::unique_ptr<MyClassImpl> specialization is instantiated inside the DLL, where MyClassImpl is a complete type.
  • Since your users share the same STL version, the linker will resolve the std::unique_ptr symbols correctly without needing to export its specialization.

Approach 2: Use a Custom Deleter (Alternative)

If you prefer not to rely on explicit template instantiation, you can define a custom deleter for std::unique_ptr that's exported from your DLL. This avoids the need to export the std::unique_ptr specialization itself, as the deleter's type is under your control.

Example Implementation:

// PImpl.h
#include <memory>

// Forward declare the deleter
template<typename T>
struct PImplDeleter;

template<typename PImpl_T> 
class __declspec(dllexport) PImpl {
private:
    std::unique_ptr<PImpl_T, PImplDeleter<PImpl_T>> m_pImpl;

public:
    PImpl();
    ~PImpl();
};

// Export your library class
class __declspec(dllexport) MyClass : private PImpl<struct MyClassImpl> {
    // ...
};

// Define the deleter (export it)
template<typename T>
struct __declspec(dllexport) PImplDeleter {
    void operator()(T* ptr) const;
};

Then in the DLL's .cpp file:

// PImpl.cpp
#include "PImpl.h"
#include "MyClassImpl.h"

template<typename T>
void PImplDeleter<T>::operator()(T* ptr) const {
    delete ptr;
}

// Explicitly instantiate the deleter for your implementation type
template struct __declspec(dllexport) PImplDeleter<MyClassImpl>;

// ... rest of PImpl and MyClass implementation ...

This approach shifts the exported symbol from std::unique_ptr to your custom deleter, which you can manage more easily. However, Approach 1 is generally simpler for most PImpl use cases.


内容的提问来源于stack exchange,提问作者user2513351

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:27:45