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 接口。这递归地适用于 dllexport
ed class.
使用的所有类型
您可能需要阅读 this article on MSDN,其中描述了考虑到容器的问题。
这个问题可以简化为:
- 如果客户端代码无法访问您的数据,请禁用此警告。
- 如果您有要由客户端代码使用的成员,请创建包装器,即
dllexport
ed 或对 dllexport
ed 方法使用额外的间接寻址。
- 通常,您可以使用 PIMPL 来隐藏非 DLL 类型,但在您的情况下它不适用,因为您使用不可导出类型来实际实现 PIMPL。
延伸阅读:
- MSDN: How to export an instantiation of a STL class
- Microsoft DLL export and C++ templates
- SO: Exporting classes containing std:: objects from a dll
- SO: How to use an exported class in an STL template?
您可以只导出 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;
这不是一个重大问题,但我想从警告中清除我的代码,所以这让我很紧张。
我一直在使用 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 接口。这递归地适用于 dllexport
ed class.
您可能需要阅读 this article on MSDN,其中描述了考虑到容器的问题。
这个问题可以简化为:
- 如果客户端代码无法访问您的数据,请禁用此警告。
- 如果您有要由客户端代码使用的成员,请创建包装器,即
dllexport
ed 或对dllexport
ed 方法使用额外的间接寻址。 - 通常,您可以使用 PIMPL 来隐藏非 DLL 类型,但在您的情况下它不适用,因为您使用不可导出类型来实际实现 PIMPL。
延伸阅读:
- MSDN: How to export an instantiation of a STL class
- Microsoft DLL export and C++ templates
- SO: Exporting classes containing std:: objects from a dll
- SO: How to use an exported class in an STL template?
您可以只导出 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;