从抽象DLL接口类继承的类上出现LNK4217警告

3
在编译两个类时,我收到了LNK4217警告:其中一个定义了一个抽象接口和一个静态工厂方法,将返回实现此接口的对象。此类通过`__declspec(dllexport)`导出。另一个类实现了该接口。以下是可复现警告的简化示例:
PublicAPI.h
#pragma once

#include <memory>

#ifdef PublicAPI_Exports
#define API_EXP __declspec (dllexport)
#else
#define API_EXP __declspec (dllimport)
#endif

class API_EXP PublicAPI
{
public:
  virtual ~PublicAPI();

  static std::shared_ptr<PublicAPI> init(const char *selection);

  virtual int compute() = 0;

protected:
  PublicAPI();
};

PublicAPI.cpp

#include "PublicAPI.h"
#include "../PrivateAPI/PrivateAPI.h"

PublicAPI::PublicAPI() {}

PublicAPI::~PublicAPI() {}

std::shared_ptr<PublicAPI> PublicAPI::init(const char *selection)
{
  return std::make_shared<PrivateAPI>(selection);
}

PrivateAPI.h

#pragma once

#include "../PublicAPI/PublicAPI.h"
#include <memory>
#include <string>

class PrivateAPI : public PublicAPI
{
public:
  PrivateAPI(std::string selection);
  virtual ~PrivateAPI();

  virtual int compute();

private:
  PrivateAPI();
  // should have more private members here, avoided to shorten the example
};

PrivateAPI.cpp

#include "PrivateAPI.h"

PrivateAPI::PrivateAPI() {}

PrivateAPI::PrivateAPI(std::string selection)
{
  // init members based on selection
}

PrivateAPI::~PrivateAPI() {}

int PrivateAPI::compute()
{
  return 42;
}

PrivateAPI被编译成静态库,PublicAPI被编译成DLL,并链接到PrivateAPIPublicAPI_ExportsPublicAPI项目中定义。

我收到的警告如下:

3>PrivateAPI.lib(PrivateAPI.obj) : warning LNK4217: locally defined symbol ??1PublicAPI@@UEAA@XZ (public: virtual __cdecl PublicAPI::~PublicAPI(void)) imported in function "public: virtual __cdecl PrivateAPI::~PrivateAPI(void)" (??1PrivateAPI@@UEAA@XZ)
3>PrivateAPI.lib(PrivateAPI.obj) : warning LNK4217: locally defined symbol ??0PublicAPI@@IEAA@XZ (protected: __cdecl PublicAPI::PublicAPI(void)) imported in function "private: __cdecl PrivateAPI::PrivateAPI(void)" (??0PrivateAPI@@AEAA@XZ)

我阅读了关于此链接器警告的Microsoft Docs和StackOverflow,但我仍然不知道出了什么问题,或者如何正确修复它 :/


你忘记在构建DLL的项目中定义PublicAPI_Exports宏了。因此,链接器看到两个定义,一个是dllimport,另一个在.cpp文件中。这是ODR违规。右键单击项目>属性>C/C++>预处理器>"预处理器定义"设置。添加PublicAPI_Exports。 - Hans Passant
@HansPassant PublicAPI_Exports 在 PublicAPI 项目中定义(如上所述)- 我刚刚双重检查过。 - guitarokh
1
链接器并不是这么说的。请确保它在所有配置和平台上都有定义。同时要记住,对于您链接的静态库,它很容易被忽视。 - Hans Passant
好的,此外在PrivateAPI项目中定义PublicAPI_Exports宏可以解决警告。只是为了确保我理解这个原因正确:PrivateAPI包含PublicAPI头文件以定义从PublicAPI继承的类。因此,在将PrivateAPI编译为静态库时,我们必须确保在PublicAPI头文件中定义dllexport,因为稍后PublicAPI(也定义了dllexport)将链接到PrivateAPI静态库,并且导出定义必须匹配! - guitarokh
1个回答

0
我能够找出两个解决方案: 解决方案1(由@HansPassant提供,我认为是更好/更通用的解决方案)
需要在PrivateAPI项目中定义PublicAPI_Exports,而不仅仅是在PublicAPI项目中定义。
我的理解是: PrivateAPI 类继承自 PublicAPI,因此它也包含了 PublicAPI.hPrivateAPI 还从 PublicAPI 继承了 dllimport/dllexport 定义。然而,PrivateAPI 被独立编译成静态库,后来只由 PublicAPI 链接。因此,当没有定义 PublicAPI_Exports 时编译 PrivateAPIPrivateAPI 类将被定义为 dllimport(通过 PublicAPI.h)。当使用已定义 PublicAPI_Exports 并链接到 PrivateAPI 静态库的 PublicAPI 项目构建时,这些定义不匹配并导致此警告。因此,必须为 PublicAPIPrivateAPI 项目都定义 PublicAPI_Exports解决方案2(适用于我的特定示例)

看起来我也只能将静态的init函数进行dllexport,该函数创建了一个满足PublicAPI接口的对象。这需要对PublicAPI.h进行以下更改:

#pragma once

#include <memory>

#ifdef PublicAPI_Exports
#define API_EXP __declspec (dllexport)
#else
#define API_EXP __declspec (dllimport)
#endif

class PublicAPI
{
public:
  virtual ~PublicAPI();

  API_EXP static std::shared_ptr<PublicAPI> init(const char *selection);

  virtual int compute() = 0;

protected:
  PublicAPI();
};

这是可行的,因为静态函数在PrivateAPI中没有再次声明。由init返回的对象似乎可以正常运行(即compute函数可访问),在包含和链接PublicAPI的客户端中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接