0

对提供者进行了更清晰的编辑。很抱歉让大家感到困惑。

这是在 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。我忘记了它到底说了什么,因为我现在无法访问我的工作电脑,但这就是它的要点。这个错误对我来说没有意义,因为走 pimpl 路线的目的是让 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)" (??0Foo@@QAE@XZ)

4

2 回答 2

2

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

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


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

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

于 2011-09-30T21:47:45.457 回答
2

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

链接器抱怨你找不到FooImpl::FooImpl(). 您在这里调用构造函数-

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

您刚刚提供了默认构造函数的声明,但没有提供它的定义

于 2011-09-30T23:35:43.547 回答