实现pimpl习语时的链接器错误

Linker error while implementing pimpl idiom

本文关键字:链接 错误 pimpl 习语 实现      更新时间:2023-10-16

编辑以提供更多的清晰度。很抱歉把大家弄糊涂了。

在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"

当我编译/link Bar.exe时,链接器抱怨它无法解析FooImpl。我记不清具体说了些什么,因为我现在无法访问我的工作电脑,但这就是要点。这个错误对我来说没有意义,因为走粉刺路线的目的是让Bar.exe不必担心FooImpl。

确切的错误如下:

1>Foo.lib(Foo.obj): error LNK2019: unresolved external symbol "public: __thiscall FooImpl::FooImpl(void)"(??0FooImpl@@QAE@XZ)在函数"public: __thiscall Foo::Foo(void)"中引用(? ? 0 foo@@qae@xz)

当你创建一个静态库时,链接器不会试图解决所有缺失的问题;它假定您稍后将把它链接到另一个库,或者可执行文件本身将提供一些缺失的功能。您一定是忘记在库项目中包含一些重要的实现文件了。

另一种可能性是pimpl实现类是模板类。模板不会立即生成代码,编译器会等待,直到您尝试在填充模板参数的情况下使用它们。您的实现文件必须包含模板的实例化,其中包含您的库将支持的参数。


在看到您的编辑后,问题是Foo::Foo构造函数需要访问FooImpl::FooImpl构造函数,但链接器不知道在哪里找到它。当链接器将库放在一起时,它不会尝试解析所有引用,因为它可能还依赖于另一个库。唯一需要解决所有问题的时候是将可执行文件放在一起的时候。所以即使Bar不需要直接知道FooImpl,它仍然依赖于它。

您可以通过以下两种方式之一来解决这个问题:要么与Foo一起从库中导出FooImpl,要么确保Foo在编译时可以访问FooImpl,将它们都放在Foo.cpp中,FooImpl位于Foo.

1>Foo.lib(Foo.obj): error LNK2019: unresolved external symbol "public: __thiscall FooImpl::FooImpl(void)"(??0FooImpl@@QAE@XZ)在函数"public: __thiscall Foo::Foo(void)"中引用(? ? 0 foo@@qae@xz)

链接器向您抱怨它找不到FooImpl::FooImpl()的定义。你在这里调用构造函数-

Foo::Foo() : pimpl_(new FooImpl())
                    // ^^^^^^^^^ Invoking the default constructor.

您只提供了默认构造函数的声明,而不是它的定义