5

我正在使用一个在其标头中定义内联函数的共享库。

这是一个简化的测试用例,如链接到库的编译单元所见(对于库所见的版本,只需替换dllimportdllexport)。

class __declspec(dllimport) MyClass {
public:
    int myFunc2();
    int myFunc1();
};

inline int MyClass::myFunc2(void) {
    return myFunc1();
}

inline int MyClass::myFunc1(void) {
    return 0;
}

编译它会给出警告:

警告:“int MyClass::myFunc1()”在被 dll 链接引用后重新声明,没有 dllimport 属性[默认启用]

请注意,定义函数的顺序很重要,因为将定义放在定义myFunc1之前myFunc2不会产生警告。

另请注意,此代码在 Visual C++ 下编译时不会出现警告。这些警告至少是针对 MinGW 的,一般来说可能是针对 GCC 的。编辑:我想到我可能必须验证警告是否未被项目设置的标志之一禁止。

我的问题是:

  • 为什么会有这种行为?
  • 在类声明myFunc1中声明可以inline解决问题。这是为什么 ?这也违反了推荐的做事方式。
  • 还有另一种(更好的?)方法来解决这个问题吗?
4

2 回答 2

6

问题的出现是因为 dllimport 的工作方式有一些魔力,这通常意味着您只需要在第一次声明时使用它。

基本上,当您将函数声明为 dllimport,然后随后使用相同声明(除了 dllimport 之外)重新声明该函数时,第二个声明会隐式获取 dllimport。如果重新声明不完全相同,则不会获得隐式 dllimport。

所以这里发生的是你首先将函数声明为 dllimport/non-inline,然后将其声明为 non-dllimport/inline。将内联添加到第一个声明可以解决问题,因为第二个声明会隐式变为 dllimport。或者,__declspec(dllimport)在第二个声明中添加 a 应该可以解决问题。

请注意,重新排序定义会消除警告,因为警告是关于在重新声明之前使用它。通过重新排序,您在重新声明之前不再使用它,因此您不会收到警告,尽管它将使用非 dllimport 版本(即,它永远不会使用 dll 中的函数版本)。

另请注意,使用内联 dllimport 是危险的。任何针对 dll 构建的程序都可能在某些地方使用内联函数,而在其他地方使用非内联函数(来自 dll)。即使这两个函数现在相同,dll 的未来版本也可能会更改并具有不同的实现。此时,如果使用新版本的 dll 运行,旧程序可能会开始出现异常。

于 2012-07-18T21:30:55.063 回答
3

克里斯多德回答的更多信息:

我用MSVC 2017和 MinGW-w64 7.2.0 进行了测试;在这两种情况下,在启用优化的情况下,尽管有警告, myFunc1()from的调用仍会解析为内联版本。myFunc2哪怕身体myFunc1被挪到了下方main()

所以我的初步结论是忽略这个警告是安全的。


为了干净编译,在 MinGW-w64 中似乎唯一可行的方法是将类定义中的函数声明标记为inline. 编译器不允许应用于__declspec(dllimport)类外函数定义。

我一直无法确定是否有 g++ 的警告标志会专门禁用此警告。

于 2018-03-02T03:11:07.623 回答