对提供者进行了编辑,使其更加清晰。道歉,使所有人感到困惑。

这是在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。

10-08 00:39