为什么编译器将内联类方法添加到lib?

时间:2018-10-10 10:05:20

标签: c++ visual-studio-2010 static-linking one-definition-rule lnk2005

考虑一个类,该类属于链接到动态库(foo.dll)的项目,并且在侧面也生成了foo.lib:

class IMP_EXP_DIRECTIVE_MACRO Foo
{
     void bar()
     {
         // do something
     }
 };

然后还有另一个项目栏(生成静态库bar.lib),其中包括此类。该项目未与foo链接。

最后,有一个应用程序项目(导致.exe),该项目同时与foo.lib和bar.lib链接。 这样做时,链接器会用LNK2005错误大喊,Foo::bar已经在foo.lib(很好)和bar.lib中都定义了,这是意外的和奇怪的。

根据https://docs.microsoft.com/en-us/cpp/build/reference/symbols?view=vs-2017输出,使用dumpbin.exe / SYMBOLS处理bar.lib时,表明Foo::bar确实在foo.lib(!)中定义。这很奇怪,因为根据这个出色的https://stackoverflow.com/a/4955288答案,在类主体中声明和定义的任何成员函数都是隐式内联的,因此不会引起ODR问题。

最后,最有趣的部分。将Foo::bar定义移至.cpp文件并将仅方法的声明留在类主体中时,问题消失了。

为什么会这样?知道做错了什么吗? 我想我已经阅读了有关此主题的整个互联网,并尝试了所有各种VS配置开关,但只有将定义移至.cpp才有用。 从原则上讲,我最终可以做到,但是这似乎并不是解决该问题的适当方法。

环境是:Visual Studio 10。

编辑:
IMP_EXP_DIRECTIVE_MACRO设置为:
构建foo.dll时出现“ __declspec(dllexport)”
“”,在构建栏时

修改2:
当在编译bar.lib中添加预处理器标志而导致使用class __declspec(dllimport) Foo而不是class Foo时,问题也消失了。
那是正确的方法吗?

1 个答案:

答案 0 :(得分:2)

该函数是可内联的

但是,如果使用其地址&Foo::bar,则编译器仍需要一个真实地址。这实际上意味着所指向的地址上必须有非内联代码。结果是编译器必须在每个转换单元中放置一个副本,链接器将选择其中一个-每个链接器输出一个。但是Foo.lib的链接程序无法预测Bar.lib中也会有一个副本,反之亦然。