实现pimpl习惯用法时出现链接器错误 [英] Linker error while implementing pimpl idiom

查看:54
本文介绍了实现pimpl习惯用法时出现链接器错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是在Windows下.

我有一个静态库,该库使用pimpl习惯用法实现了一个类.pimpl头不仅被使用的代码使用,而且还链接到静态库.但是,当我编译使用代码(.exe)时,链接器会抱怨pimpl标头应该隐藏的实现类上无法解析的外部变量.

这怎么可能?

 //Foo.lib//Foo.h类FooImpl;Foo类{std :: shared_ptr< FooImpl>pimpl_;上市:Foo();};//Foo.cpp#include"Foo.h"#include"FooImpl.h"Foo :: Foo():pimpl_(新的FooImpl()){}//这是在Bar.exe中的用法//Bar.exe链接到Foo.lib//Bar.h#include"Foo.h"班级酒吧{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./p>

Edited to provider a little more clarity. Apologies for confusing everyone.

This is under Windows.

I have a static library that implements a class using the pimpl idiom. The pimpl header is not only used by the consuming code but it also links against the static library. Yet, when I compile the consuming code (.exe), the linker complains about unresolved externals on the implementation class that the pimpl header supposedly hides.

How can this be possible?

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

When I compile/link Bar.exe, the linker complains that it is unable to resolve FooImpl. I forget what it said exactly since I don't have access to my work PC now but that's the gist of it. The error didn't make sense to me because the objective of going the pimpl route was so that Bar.exe didn't have to worry about FooImpl.

The exact error goes like this:

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

解决方案

When you create a static library, the linker doesn't try to resolve everything that is missing; it assumes you'll be linking it later to another library, or that the executable itself will contribute some missing function. You must have forgotten to include some crucial implementation file in the library project.

The other possibility is that the pimpl implementation class is a template class. Templates don't generate code immediately, the compiler waits until you try to use them with the template parameters filled in. Your implementation file must include an instantiation of the template with the parameters that will be supported by your library.


After seeing your edit, the problem is that the Foo::Foo constructor needs access to the FooImpl::FooImpl constructor but the linker doesn't know where to find it. When the linker puts together the library, it doesn't try to resolve all of the references at that time, because it might have dependencies on yet another library. The only time that everything needs to be resolved is when it's putting together the executable. So even though Bar doesn't need to know about FooImpl directly, it still has a dependency on it.

You can resolve this in one of two ways: either export FooImpl from the library along with Foo, or make sure that Foo has access to FooImpl at compile time by putting them both in Foo.cpp with FooImpl coming before Foo.

这篇关于实现pimpl习惯用法时出现链接器错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆