【问题标题】:std::unique_ptr pimpl in dll generates C4251 with visual studiodll 中的 std::unique_ptr pimpl 使用 Visual Studio 生成 C4251
【发布时间】:2015-11-12 21:36:53
【问题描述】:

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

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

// 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 没有问题,使用旧的原始指针版本会让我心碎..

【问题讨论】:

  • 这个答案可能会有所帮助:stackoverflow.com/questions/24635255/…
  • 作为测试,能不能把std::unique_ptr&lt;FrameworkImpl&gt; impl_;换成struct do_nothing{ template&lt;class T&gt; void operator()(T&amp;&amp;)const{}; }; std::unique_ptr&lt;FrameworkImpl, do_nothing&gt; impl_;——我想看看是不是和默认删除器有关。 (如果这确实编译,您还没有完成!但它是诊断性的。)还要注意警告可能是虚假的。
  • @Yakk 相同的警告,但使用 'std::unique_ptr' 代替

标签: c++ c++11 visual-c++ pimpl-idiom


【解决方案1】:

对于使用来自std 的模板的 DLL 类来说,这是一个非常常见的问题。

为什么会这样?

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

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

您可能想阅读this article on MSDN,它描述了考虑到容器的这个问题。

这个问题可以简化为:

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

进一步阅读:

【讨论】:

  • 或者,写 30 行 simple_unique_ptr 并下注?还是60?对于一个简单的 RAII 指针所有者来说,这并不多。
  • 是的,这可能是最好的解决方案。当我们开始设计我们的渲染引擎(被设计成一组 DLL 模块)时,我们从实现自定义容器、字符串和 RAII 原语开始。费用:4人工作3天。好处:巨大的性能提升。 DLL 接口没有问题。
  • 什么?由于某些 DLL 问题,您使用手卷容器而不是标准容器?通常在 linux 中处理这些事情的方式是 (1) 没有 DLL_export (2) 客户知道他们需要使用由相同编译器和标准库构建的库,如果他们不这样做,那么大多数由于这个问题,他们很可能会遇到链接器错误。你会认真建议每个 DLL 都应该发布自己的私有容器集合来复制标准库吗?
  • 自定义容器s?真的,你为什么需要更多vector。字符串只是 vector 加一些糖。有效的地图是vector 加一些糖。我想需要 RAII。
  • @ChrisBeck 在 Windows 领域,DLL 应该是可移植的(在操作系统版本、编译器版本、系统等之间)。限制使用相同的编译器版本和怪癖进行编译被认为过于严格。如果您要运送一些组成一组程序的 DLL 集合,那么该级别的要求是合理的:否则,不是。所以你会得到导出 COM 接口和其他可爱的 DLL。
【解决方案2】:

您可以只导出公共方法,而不是导出整个类:

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_;
};

【讨论】:

    【解决方案3】:

    解决方案是在 impl 声明和 Olga Perederieieva 答案组合之后声明构造函数/析构函数

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

    标题:

    #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;
    

    【讨论】:

      猜你喜欢
      • 2015-07-16
      • 2020-07-06
      • 1970-01-01
      • 2015-05-01
      • 2015-04-15
      • 1970-01-01
      • 1970-01-01
      • 2014-08-30
      • 2011-05-09
      相关资源
      最近更新 更多