std::unique_ptr dll 中的 pimpl 使用 visual studio 生成 C4251

std::unique_ptr pimpl in dll generates C4251 with visual studio

这不是一个重大问题,但我想从警告中清除我的代码,所以这让我很紧张。

我一直在使用 c++11 版本的 pimpl idiom 以常规方式隐藏我的库的 class 实现。

// dll header
class FrameworkImpl;

class EXPORT_API Framework
{
    Framework(const Framework&) = delete;
    Framework& operator=(const Framework&) = delete;
    Framework(Framework&&) = delete;
    Framework& operator=(Framework&&) = delete;

public:
    Framework();
    ~Framework();

private:
    std::unique_ptr<FrameworkImpl> impl_;
};

// application implementation
int main()
{
    std::unique_ptr<Framework> test = std::make_unique<Framework>();
}

一切都会好的,但我会不断收到警告:

warning C4251: 'Framework::impl_': class 'std::unique_ptr<FrameworkImpl,std::default_delete<_Ty>>' needs to have dll-interface to be used by clients of class 'Framework'

所以我尝试必须添加:

template class EXPORT_API std::unique_ptr<FrameworkImpl>;

在前向声明之前,但警告只会更改为:

warning C4251: 'std::_Unique_ptr_base<_Ty,_Dx>::_Mypair': class 'std::_Compressed_pair<_Dx,FrameworkImpl *,true>' needs to have dll-interface to be used by clients of class 'std::_Unique_ptr_base<_Ty,_Dx>'

我从 VS2010 开始就遇到了这个问题,但我想不出解决这个问题的好方法。 gcc 或 clang 没有问题,使用旧的原始指针版本会让我心碎..

这是 DLL classes 的一个非常常见的问题,它使用来自 std.

的模板

为什么会这样?

原因很简单:标准只规定了保证限制要求。所以你可以肯定,每个 C++ 11 编译器都会提供 std::unique_ptr,其外观和工作方式与 on this page 描述的一样。但其他一切都取决于实现。

主要问题是,不同的实现可能(并且通常会)对特定类型使用完全不同的结构。他们使用额外的辅助变量、不同的布局等等。这甚至在同一编译器的两个版本之间也可能不同。因此,如果客户端代码以任何方式触及 class 的成员变量,则需要为它们提供 DLL 接口。这递归地适用于 dllexported class.

使用的所有类型

您可能需要阅读 this article on MSDN,其中描述了考虑到容器的问题。

这个问题可以简化为:

  • 如果客户端代码无法访问您的数据,请禁用此警告。
  • 如果您有要由客户端代码使用的成员,请创建包装器,即 dllexported 或对 dllexported 方法使用额外的间接寻址。
  • 通常,您可以使用 PIMPL 来隐藏非 DLL 类型,但在您的情况下它不适用,因为您使用不可导出类型来实际实现 PIMPL。

延伸阅读:

您可以只导出 public 方法,而不是导出整个 class:

class Framework
{
    Framework(const Framework&) = delete;
    Framework& operator=(const Framework&) = delete;
    Framework(Framework&&) = delete;
    Framework& operator=(Framework&&) = delete;

public:
    EXPORT_API Framework();
    EXPORT_API ~Framework();

private:
    std::unique_ptr<FrameworkImpl> impl_;
};

解决方案是在 impl 声明之后声明 constructor/destructor 并结合 Olga Perederiieeva 的回答

详细解释和示例请参考这个website

Header:

#include <memory>

class FridgeImpl;

class Fridge
{
public:
   DLL_EXPORT Fridge();
   DLL_EXPORT ~Fridge();
   DLL_EXPORT void coolDown();
private:
   std::unique_ptr<FridgeImpl> impl_;
};

实施:

#include "Engine.h"
#include "Fridge.h"

class FridgeImpl
{
public:
   void coolDown()
   {
      /* ... */
   }
private:
   Engine engine_;
};

Fridge::Fridge() : impl_(new FridgeImpl) {}

Fridge::~Fridge() = default;