对提供者进行了编辑,使其更加清晰。道歉,使所有人感到困惑。
这是在Windows下。
我有一个静态库,使用pimpl习惯用法实现了一个类。 pimpl标头不仅被使用的代码使用,而且还链接到静态库。但是,当我编译使用代码(.exe)时,链接器会抱怨pimpl标头应该隐藏的实现类上尚未解决的外部组件。
这怎么可能?
// Foo.lib
// Foo.h
class FooImpl;
class Foo
{
std::shared_ptr<FooImpl> pimpl_;
public:
Foo();
};
// Foo.cpp
#include "Foo.h"
#include "FooImpl.h"
Foo::Foo() : pimpl_(new FooImpl())
{
}
// This is how its used in Bar.exe
// Bar.exe links against Foo.lib
// Bar.h
#include "Foo.h"
class Bar
{
Foo access_foo_;
};
// Bar.cpp
#include "Bar.h"
当我编译/链接Bar.exe时,链接器抱怨它无法解析FooImpl。我忘记了它的确切含义,因为我现在无法访问我的工作PC,但这就是要点。该错误对我而言没有任何意义,因为采用pimpl路由的目的是让Bar.exe不必担心FooImpl。
确切的错误是这样的:
1> Foo.lib(Foo.obj):错误LNK2019:无法解析的外部符号“ public:__thiscall FooImpl :: FooImpl(void)”(?? 0FooImpl @@ QAE @ XZ)在函数“ public:__thiscall Foo :: Foo”中引用(void)”(?? 0Foo @@ QAE @ XZ)
最佳答案
创建静态库时,链接器不会尝试解决所有丢失的问题;它假定您稍后将其链接到另一个库,或者可执行文件本身将贡献一些缺少的功能。您一定忘记了在库项目中包含一些关键的实现文件。
另一个可能性是pimpl实现类是模板类。模板不会立即生成代码,编译器会等到您尝试使用填充了模板参数的模板时使用。您的实现文件必须包括模板的实例化,该模板带有库支持的参数。
看到您的编辑后,问题在于Foo :: Foo构造函数需要访问FooImpl :: FooImpl构造函数,但链接器不知道在哪里可以找到它。当链接程序将库放在一起时,它当时不会尝试解析所有引用,因为它可能与另一个库有依赖性。唯一需要解决的事情就是将可执行文件放在一起。因此,即使Bar不需要直接了解FooImpl,它仍然具有依赖性。
您可以通过以下两种方法之一解决此问题:从库中将FooImpl与Foo一起导出,或者通过将二者都放入Foo.cpp中,并且FooImpl放在Foo之前,确保Foo在编译时可以访问FooImpl。