什么是未定义的引用/未解析的外部符号错误以及如何修复它?
什么是未定义的引用/未解析的外部符号错误?常见原因有哪些?如何修复和预防这些错误?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
什么是未定义的引用/未解析的外部符号错误?常见原因有哪些?如何修复和预防这些错误?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(30)
当我们在程序中引用对象名称(类、函数、变量等)并且链接器尝试在以下位置搜索它时找不到其定义时,就会出现“未定义引用”错误所有链接的目标文件和库。
因此,当链接器找不到链接对象的定义时,它会发出“未定义的引用”错误。从定义中可以清楚地看出,此错误发生在链接过程的后期阶段。导致“未定义引用”错误的原因有多种。
一些可能的原因(更常见):
#1) 没有为对象提供定义
这是导致“未定义引用”错误的最简单原因。程序员只是忘记定义对象。
考虑以下 C++ 程序。这里我们只指定了函数的原型,然后在主函数中使用它。
输出:
因此,当我们编译该程序时,会发出链接器错误,提示“对 'func1()' 的未定义引用”。
为了消除这个错误,我们通过提供函数 func1 的定义来更正程序如下。现在程序给出了适当的输出。
输出:
#2) 使用的对象的错误定义(签名不匹配)
导致“未定义引用”错误的另一个原因是我们指定了错误的定义。我们在程序中使用任何对象,其定义都是不同的。
考虑以下 C++ 程序。这里我们调用了 func1()。它的原型是int func1()。但它的定义与其原型并不相符。正如我们所看到的,函数的定义包含函数的参数。
这样当程序编译时,由于原型和函数调用匹配,所以编译成功。但是,当链接器尝试将函数调用与其定义链接时,它发现问题并发出“未定义引用”错误。
输出:
因此,为了防止此类错误,我们只需交叉检查程序中所有对象的定义和用法是否匹配。
#3) 对象文件未正确链接
此问题还会导致“未定义的引用”错误。在这里,我们可能有多个源文件,我们可能会独立编译它们。完成此操作后,对象无法正确链接,并导致“未定义的引用”。
考虑以下两个 C++ 程序。在第一个文件中,我们使用第二个文件中定义的“print()”函数。当我们单独编译这些文件时,第一个文件为 print 函数提供了“未定义的引用”,而第二个文件为 main 函数提供了“未定义的引用”。
输出:
输出:
解决此错误的方法是同时编译这两个文件(例如,使用 g++)。
除了已经讨论过的原因之外,“未定义的引用”也可能由于以下原因而发生。
#4) 错误的项目类型
当我们在 Visual Studio 等 C++ IDE 中指定错误的项目类型并尝试做项目不期望的事情时,我们会得到“未定义的引用”。
#5) 无库
如果程序员没有正确指定库路径或完全忘记指定它,那么我们会得到一个“未定义的引用”,用于程序从库中使用的所有引用。
#6) 依赖文件未编译
程序员必须确保我们事先编译了项目的所有依赖项,以便在编译项目时,编译器找到所有依赖项并编译成功。如果缺少任何依赖项,则编译器会给出“未定义的引用”。
除了上面讨论的原因之外,“未定义的引用”错误也可能在许多其他情况下发生。但最重要的是,程序员犯了错误,为了防止这种错误,他们应该被纠正。
An “Undefined Reference” error occurs when we have a reference to object name (class, function, variable, etc.) in our program and the linker cannot find its definition when it tries to search for it in all the linked object files and libraries.
Thus when the linker cannot find the definition of a linked object, it issues an “undefined reference” error. As clear from definition, this error occurs in the later stages of the linking process. There are various reasons that cause an “undefined reference” error.
Some possible reason(more frequent):
#1) No Definition Provided For Object
This is the simplest reason for causing an “undefined reference” error. The programmer has simply forgotten to define the object.
Consider the following C++ program. Here we have only specified the prototype of function and then used it in the main function.
Output:
So when we compile this program, the linker error that says “undefined reference to ‘func1()’” is issued.
In order to get rid of this error, we correct the program as follows by providing the definition of the function func1. Now the program gives the appropriate output.
Output:
#2) Wrong Definition (signatures don’t match) Of Objects Used
Yet another cause for “undefined reference” error is when we specify wrong definitions. We use any object in our program and its definition is something different.
Consider the following C++ program. Here we have made a call to func1 (). Its prototype is int func1 (). But its definition does not match with its prototype. As we see, the definition of the function contains a parameter to the function.
Thus when the program is compiled, the compilation is successful because of the prototype and function call match. But when the linker is trying to link the function call with its definition, it finds the problem and issues the error as “undefined reference”.
Output:
Thus to prevent such errors, we simply cross-check if the definitions and usage of all the objects are matching in our program.
#3) Object Files Not Linked Properly
This issue can also give rise to the “undefined reference” error. Here, we may have more than one source files and we might compile them independently. When this is done, the objects are not linked properly and it results in “undefined reference”.
Consider the following two C++ programs. In the first file, we make use of the “print ()” function which is defined in the second file. When we compile these files separately, the first file gives “undefined reference” for the print function, while the second file gives “undefined reference” for the main function.
Output:
Output:
The way to resolve this error is to compile both the files simultaneously (For example, by using g++).
Apart from the causes already discussed, “undefined reference” may also occur because of the following reasons.
#4) Wrong Project Type
When we specify wrong project types in C++ IDEs like the visual studio and try to do things that the project does not expect, then, we get “undefined reference”.
#5) No Library
If a programmer has not specified the library path properly or completely forgotten to specify it, then we get an “undefined reference” for all the references the program uses from the library.
#6) Dependent Files Are Not Compiled
A programmer has to ensure that we compile all the dependencies of the project beforehand so that when we compile the project, the compiler finds all the dependencies and compiles successfully. If any of the dependencies are missing then the compiler gives “undefined reference”.
Apart from the causes discussed above, the “undefined reference” error can occur in many other situations. But the bottom line is that the programmer has got the things wrong and in order to prevent this error they should be corrected.
假设您有以下代码:
编译
b.cpp
时,编译器简单地假设get()
符号在某处定义,但事实并非如此还不关心在哪里。链接阶段负责查找符号并正确链接从a.cpp
和b.cpp
生成的目标文件。如果
a.cpp
没有定义get
,您将收到一个链接器错误,指出“未定义的引用”或“无法解析的外部符号”。C++ 标准措辞
编译 C++ 程序分为 [lex.phases],最后一个是相关的:
有关这些阶段的摘要,请参阅 Keith Thompson 的回答。
指定的错误发生在编译的最后阶段,通常称为链接。它基本上意味着您将一堆源文件编译成目标文件或库,现在您想让它们一起工作。
实践中的链接器错误
如果您使用 Microsoft Visual Studio,您将看到项目生成
.lib
文件。它们包含导出符号表和导入符号表。导入的符号根据您链接的库进行解析,导出的符号则提供给使用该.lib
(如果有)的库。其他编译器/平台也存在类似的机制。
常见的错误消息有 Microsoft Visual Studio 的
错误 LNK2001
、错误 LNK1120
、错误 LNK2019
和未定义的引用为GCC的
symbolName。该代码:
使用 GCC 会生成以下错误:
使用 Microsoft Visual Studio 会生成类似错误:
常见原因
#pragma
(Microsoft Visual Studio) 时的 .lib 扩展名UNICODE
定义Say you have the following code:
When compiling
b.cpp
, the compiler simply assumes thatget()
symbol was defined somewhere, but it doesn't yet care where. The linking phase is responsible for finding the symbol and correctly linking the object files produced froma.cpp
andb.cpp
.If
a.cpp
didn't defineget
, you would get a linker error saying "undefined reference" or "unresolved external symbol".C++ Standard Wording
Compiling a C++ program takes place in several phases specified in [lex.phases], the last of which is relevant:
See Keith Thompson's answer for a summary of these phases.
The specified errors occur during this last stage of compilation, most commonly referred to as linking. It basically means that you compiled a bunch of source files into object files or libraries, and now you want to get them to work together.
Linker Errors in Practice
If you're using Microsoft Visual Studio, you'll see that projects generate
.lib
files. These contain a table of exported symbols, and a table of imported symbols. The imported symbols are resolved against the libraries you link against, and the exported symbols are provided for the libraries that use that.lib
(if any).Similar mechanisms exist for other compilers/ platforms.
Common error messages are
error LNK2001
,error LNK1120
,error LNK2019
for Microsoft Visual Studio andundefined reference to
symbolName for GCC.The code:
will generate the following errors with GCC:
and similar errors with Microsoft Visual Studio:
Common Causes
#pragma
(Microsoft Visual Studio)UNICODE
definitions类成员:
纯虚拟析构函数需要一个实现。
声明析构函数为 pure 仍然需要定义它(与常规函数不同):
发生这种情况是因为当对象被隐式销毁时会调用基类析构函数,因此需要定义。
虚拟
方法必须实现或定义为纯方法。这与没有定义的非
虚拟
方法类似,但增加了以下推理:pure 声明会生成一个虚拟 vtable,如果不使用该函数,您可能会收到链接器错误:
为此,请将
X::foo()
声明为 pure:Non-
virtual
类成员即使未显式使用,某些成员也需要定义:
以下情况会产生错误:
实现可以是内联的,在类定义本身中:
或外部:
如果实现位于类定义之外,但在标头中,这些方法必须标记为
内联
以防止多重定义。所有使用的成员方法如果使用的话都需要定义。
一个常见的错误是忘记限定名称:
定义应该是
静态
数据成员必须在类外部的单个翻译单元中定义:可以为类定义中的
static
const
整型或枚举类型的数据成员;但是,该成员的 odr 使用仍然需要如上所述的命名空间范围定义。 C++11 允许在类内部对所有static const
数据成员进行初始化。Class members:
A pure
virtual
destructor needs an implementation.Declaring a destructor pure still requires you to define it (unlike a regular function):
This happens because base class destructors are called when the object is destroyed implicitly, so a definition is required.
virtual
methods must either be implemented or defined as pure.This is similar to non-
virtual
methods with no definition, with the added reasoning thatthe pure declaration generates a dummy vtable and you might get the linker error without using the function:
For this to work, declare
X::foo()
as pure:Non-
virtual
class membersSome members need to be defined even if not used explicitly:
The following would yield the error:
The implementation can be inline, in the class definition itself:
or outside:
If the implementation is outside the class definition, but in a header, the methods have to be marked as
inline
to prevent a multiple definition.All used member methods need to be defined if used.
A common mistake is forgetting to qualify the name:
The definition should be
static
data members must be defined outside the class in a single translation unit:An initializer can be provided for a
static
const
data member of integral or enumeration type within the class definition; however, odr-use of this member will still require a namespace scope definition as described above. C++11 allows initialization inside the class for allstatic const
data members.未能链接到适当的库/目标文件或编译实现文件
通常,每个翻译单元都会生成一个目标文件,其中包含该翻译单元中定义的符号的定义。
要使用这些符号,您必须链接这些目标文件。
在gcc下,您可以指定要在命令行中链接在一起的所有目标文件,或者将实现文件编译在一起。
-l...
必须位于任何.o
/.c
/.cpp
文件的右侧。这里的
libraryName
只是库的简单名称,没有特定于平台的添加。例如,在 Linux 上,库文件通常称为libfoo.so
,但您只需编写-lfoo
。在 Windows 上,同一文件可能称为 foo.lib,但您将使用相同的参数。您可能必须使用-L 添加可以找到这些文件的目录。确保
-l
或-L
后不要写入空格。对于 Xcode:添加用户标题搜索路径 ->添加库搜索路径->将实际的库引用拖放到项目文件夹中。
在MSVS下,添加到项目中的文件会自动将其目标文件链接在一起,并会生成
lib
文件(常用)。要在单独的项目中使用这些符号,您需要需要在项目设置中包含
lib
文件。这是在项目属性的“链接器”部分的“输入 ->”中完成的。附加依赖项。 (lib
文件的路径应该是添加到
Linker ->一般->其他库目录
)当使用随lib
文件提供的第三方库时,不这样做通常会导致错误。您也可能忘记将文件添加到编译中,在这种情况下将不会生成目标文件。在gcc中,您可以将文件添加到命令行。在 MSVS 中,将文件添加到项目中将使其自动编译(尽管可以手动将文件单独从构建中排除)。
在 Windows 编程中,未链接必要库的标志是未解析符号的名称以
__imp_
开头。在文档中查找函数的名称,它应该说明您需要使用哪个库。例如,MSDN 将信息放在每个函数底部名为“库”的部分中的框中。Failure to link against appropriate libraries/object files or compile implementation files
Commonly, each translation unit will generate an object file that contains the definitions of the symbols defined in that translation unit.
To use those symbols, you have to link against those object files.
Under gcc you would specify all object files that are to be linked together in the command line, or compile the implementation files together.
-l...
must be to the right of any.o
/.c
/.cpp
files.The
libraryName
here is just the bare name of the library, without platform-specific additions. So e.g. on Linux library files are usually calledlibfoo.so
but you'd only write-lfoo
. On Windows that same file might be calledfoo.lib
, but you'd use the same argument. You might have to add the directory where those files can be found using-L‹directory›
. Make sure to not write a space after-l
or-L
.For Xcode: Add the User Header Search Paths -> add the Library Search Path -> drag and drop the actual library reference into the project folder.
Under MSVS, files added to a project automatically have their object files linked together and a
lib
file would be generated (in common usage). To use the symbols in a separate project, you'dneed to include the
lib
files in the project settings. This is done in the Linker section of the project properties, inInput -> Additional Dependencies
. (the path to thelib
file should beadded in
Linker -> General -> Additional Library Directories
) When using a third-party library that is provided with alib
file, failure to do so usually results in the error.It can also happen that you forget to add the file to the compilation, in which case the object file won't be generated. In gcc you'd add the files to the command line. In MSVS adding the file to the project will make it compile it automatically (albeit files can, manually, be individually excluded from the build).
In Windows programming, the tell-tale sign that you did not link a necessary library is that the name of the unresolved symbol begins with
__imp_
. Look up the name of the function in the documentation, and it should say which library you need to use. For example, MSDN puts the information in a box at the bottom of each function in a section called "Library".声明但未定义变量或函数。
典型的变量声明是
由于这只是一个声明,因此需要单个定义。相应的定义是:
例如,以下内容将产生错误:
类似的注释也适用于函数。声明函数而不定义它会导致错误:
请注意您实现的函数与您声明的函数完全匹配。例如,您的 cv 限定符可能不匹配:
其他不匹配的示例包括
来自编译器的错误消息通常会为您提供已声明但从未定义的变量或函数的完整声明。将其与您提供的定义进行仔细比较。 确保每个细节都匹配。
Declared but did not define a variable or function.
A typical variable declaration is
As this is only a declaration, a single definition is needed. A corresponding definition would be:
For example, the following would generate an error:
Similar remarks apply to functions. Declaring a function without defining it leads to the error:
Be careful that the function you implement exactly matches the one you declared. For example, you may have mismatched cv-qualifiers:
Other examples of mismatches include
The error message from the compiler will often give you the full declaration of the variable or function that was declared but never defined. Compare it closely to the definition you provided. Make sure every detail matches.
指定相互依赖的链接库的顺序是错误的。
如果库相互依赖,则库链接的顺序很重要。一般来说,如果库
A
依赖于库B
,那么libA
必须出现在libB
之前code> 在链接器标志中。例如:
创建库:
编译:
所以再次重复一下,顺序确实很重要!
The order in which interdependent linked libraries are specified is wrong.
The order in which libraries are linked DOES matter if the libraries depend on each other. In general, if library
A
depends on libraryB
, thenlibA
MUST appear beforelibB
in the linker flags.For example:
Create the libraries:
Compile:
So to repeat again, the order DOES matter!
符号在 C 程序中定义并在 C++ 代码中使用。
函数(或变量)
void foo()
是在 C 程序中定义的,并且您尝试在 C++ 程序中使用它:C++ 链接器期望名称被破坏,因此您必须声明该函数同样
,函数(或变量)
void foo()
不是在 C 程序中定义,而是在 C++ 中定义,但具有 C 链接:并且您尝试在 C++ 程序中使用它C++ 链接。
如果整个库包含在头文件中(并且被编译为 C 代码);包含内容需要如下;
Symbols were defined in a C program and used in C++ code.
The function (or variable)
void foo()
was defined in a C program and you attempt to use it in a C++ program:The C++ linker expects names to be mangled, so you have to declare the function as:
Equivalently, instead of being defined in a C program, the function (or variable)
void foo()
was defined in C++ but with C linkage:and you attempt to use it in a C++ program with C++ linkage.
If an entire library is included in a header file (and was compiled as C code); the include will need to be as follows;
什么是“未定义的引用/未解析的外部符号”
我将尝试解释什么是“未定义的引用/未解析的外部符号”。
例如,我们有一些代码
和
Make 目标文件
在汇编器阶段之后,我们有一个目标文件,其中包含要导出的任何符号。
看看
我从输出中拒绝了一些行的符号,因为它们并不重要
所以,我们看到了要导出的以下符号。
src2.cpp 不导出任何内容,我们也没有看到它的符号
链接我们的目标文件
并运行它
链接器会看到导出的符号并链接它。现在我们尝试像这里一样取消注释 src2.cpp 中的行
并重建目标文件
OK(没有错误),因为我们只构建目标文件,链接尚未完成。
尝试链接
它发生是因为我们的 local_var_name 是静态的,即它对其他模块不可见。
现在更深入。获取翻译阶段输出
因此,我们看到 local_var_name 没有标签,这就是链接器找不到它的原因。但我们是黑客:)我们可以修复它。在文本编辑器中打开 src1.s 并更改
为
ie,您应该如下所示
我们更改了 local_var_name 的可见性并将其值设置为 456789。
尝试从中构建一个目标文件
,好的,请参阅 readelf 输出(符号),
现在 local_var_name 已绑定 GLOBAL (是 LOCAL)
链接
并运行它
,我们破解它:)
因此,结果 - 一个“未定义的引用/未解析的外部符号”当链接器在目标文件中找不到全局符号时,会发生错误”。
what is an "undefined reference/unresolved external symbol"
I'll try to explain what is an "undefined reference/unresolved external symbol".
For example we have some code
and
Make object files
After the assembler phase we have an object file, which contains any symbols to export.
Look at the symbols
I've rejected some lines from output, because they do not matter
So, we see follow symbols to export.
src2.cpp exports nothing and we have seen no its symbols
Link our object files
and run it
Linker sees exported symbols and links it. Now we try to uncomment lines in src2.cpp like here
and rebuild an object file
OK (no errors), because we only build object file, linking is not done yet.
Try to link
It has happened because our local_var_name is static, i.e. it is not visible for other modules.
Now more deeply. Get the translation phase output
So, we've seen there is no label for local_var_name, that's why linker hasn't found it. But we are hackers :) and we can fix it. Open src1.s in your text editor and change
to
i.e. you should have like below
we have changed the visibility of local_var_name and set its value to 456789.
Try to build an object file from it
ok, see readelf output (symbols)
now local_var_name has Bind GLOBAL (was LOCAL)
link
and run it
ok, we hack it :)
So, as a result - an "undefined reference/unresolved external symbol error" happens when the linker cannot find global symbols in the object files.
如果所有其他方法都失败,请重新编译。
最近,我只需重新编译有问题的文件即可消除 Visual Studio 2012 中未解决的外部错误。当我重新构建时,错误消失了。
当两个(或更多)库具有循环依赖关系时,通常会发生这种情况。库 A 尝试使用 B.lib 中的符号,库 B 尝试使用 A.lib 中的符号。一开始两者都不存在。当您尝试编译 A 时,链接步骤将失败,因为它找不到 B.lib。将会生成A.lib,但不会生成dll。然后编译 B,它将成功并生成 B.lib。现在可以重新编译 A,因为现在已找到 B.lib。
If all else fails, recompile.
I was recently able to get rid of an unresolved external error in Visual Studio 2012 just by recompiling the offending file. When I re-built, the error went away.
This usually happens when two (or more) libraries have a cyclic dependency. Library A attempts to use symbols in B.lib and library B attempts to use symbols from A.lib. Neither exist to start off with. When you attempt to compile A, the link step will fail because it can't find B.lib. A.lib will be generated, but no dll. You then compile B, which will succeed and generate B.lib. Re-compiling A will now work because B.lib is now found.
模板实现不可见。
非专用模板的定义必须对所有使用它们的翻译单元可见。这意味着你不能分离模板的定义
到实施文件。如果必须分离实现,通常的解决方法是使用一个
impl
文件,将其包含在标头末尾声明模板。常见的情况是:
要解决此问题,您必须将
X::foo
的定义移至头文件或使用它的翻译单元可见的某个位置。专用模板可以在实现文件中实现,并且实现不必是可见的,但必须事先声明专用化。
有关进一步的说明和另一种可能的解决方案(显式实例化),请参阅此问题和解答。
Template implementations not visible.
Unspecialized templates must have their definitions visible to all translation units that use them. That means you can't separate the definition of a template
to an implementation file. If you must separate the implementation, the usual workaround is to have an
impl
file which you include at the end of the header thatdeclares the template. A common situation is:
To fix this, you must move the definition of
X::foo
to the header file or some place visible to the translation unit that uses it.Specialized templates can be implemented in an implementation file and the implementation doesn't have to be visible, but the specialization must be previously declared.
For further explanation and another possible solution (explicit instantiation) see this question and answer.
这是每个 VC++ 程序员一次又一次看到的最令人困惑的错误消息之一。让我们先把事情弄清楚。
A.什么是符号?
简而言之,符号就是名称。它可以是变量名、函数名、类名、typedef 名称,或者除了那些属于 C++ 语言的名称和符号之外的任何名称。它是用户定义的或由依赖库(另一个用户定义的)引入的。
B.什么是外部?
在VC++中,每个源文件(.cpp、.c等)都被视为一个翻译单元,编译器一次编译一个单元,并为当前翻译单元生成一个目标文件(.obj)。 (请注意,此源文件包含的每个头文件都将被预处理,并将被视为此翻译单元的一部分)翻译单元内的所有内容都被视为内部,其他所有内容都被视为外部。在 C++ 中,您可以使用
extern
、__declspec (dllimport)
等关键字来引用外部符号。C.什么是“决心”?
解决是一个链接时间术语。在链接时,链接器尝试为目标文件中无法在内部找到其定义的每个符号查找外部定义。此搜索过程的范围包括:
指定为该构建应用程序的附加依赖项。
这个搜索过程称为解析。
D.最后,为什么是未解析的外部符号?
如果链接器无法找到内部没有定义的符号的外部定义,则会报告“无法解析的外部符号”错误。
E. LNK2019的可能原因:未解析的外部符号错误。
我们已经知道这个错误是由于链接器未能找到外部符号的定义,可能的原因可以排序为:
例如,如果我们在 a.cpp 中定义了一个名为 foo 的函数:
在 b.cpp 中我们想要调用函数 foo,所以我们添加
声明函数 foo(),并在另一个函数体中调用它,例如
bar()
:现在,当您构建此代码时,您将收到 LNK2019 错误抱怨foo 是一个未解析的符号。在这种情况下,我们知道 foo() 在 a.cpp 中有其定义,但与我们调用的不同(返回值不同)。这就是定义存在的情况。
如果我们要调用库中的某些函数,但导入库没有添加到附加依赖列表中(设置自:
项目|属性|配置属性|链接器|输入|附加依赖
) 您的项目设置。现在链接器将报告 LNK2019,因为当前搜索范围中不存在该定义。This is one of most confusing error messages that every VC++ programmers have seen time and time again. Let’s make things clarity first.
A. What is symbol?
In short, a symbol is a name. It can be a variable name, a function name, a class name, a typedef name, or anything except those names and signs that belong to C++ language. It is user defined or introduced by a dependency library (another user-defined).
B. What is external?
In VC++, every source file (.cpp,.c,etc.) is considered as a translation unit, the compiler compiles one unit at a time, and generate one object file(.obj) for the current translation unit. (Note that every header file that this source file included will be preprocessed and will be considered as part of this translation unit)Everything within a translation unit is considered as internal, everything else is considered as external. In C++, you may reference an external symbol by using keywords like
extern
,__declspec (dllimport)
and so on.C. What is “resolve”?
Resolve is a linking-time term. In linking-time, linker attempts to find the external definition for every symbol in object files that cannot find its definition internally. The scope of this searching process including:
specified as additional dependencies of this building application.
This searching process is called resolve.
D. Finally, why Unresolved External Symbol?
If the linker cannot find the external definition for a symbol that has no definition internally, it reports an Unresolved External Symbol error.
E. Possible causes of LNK2019: Unresolved External Symbol error.
We already know that this error is due to the linker failed to find the definition of external symbols, the possible causes can be sorted as:
For example, if we have a function called foo defined in a.cpp:
In b.cpp we want to call function foo, so we add
to declare function foo(), and call it in another function body, say
bar()
:Now when you build this code you will get a LNK2019 error complaining that foo is an unresolved symbol. In this case, we know that foo() has its definition in a.cpp, but different from the one we are calling(different return value). This is the case that definition exists.
If we want to call some functions in a library, but the import library is not added into the additional dependency list (set from:
Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
) of your project setting. Now the linker will report a LNK2019 since the definition does not exist in current searching scope.跨模块/dll 错误地导入/导出方法/类(特定于编译器)。
MSVS 要求您使用 __declspec(dllexport) 和 __declspec(dllimport) 指定要导出和导入的符号。
这种双重功能通常通过使用宏来获得:
宏
THIS_MODULE
只能在导出该函数的模块中定义。这样,声明:扩展为
并告诉编译器导出该函数,因为当前模块包含其定义。当将声明包含在不同的模块中时,它将扩展为
并告诉编译器该定义位于您链接的库之一中(另请参阅1))。
您可以类似地导入/导出类:
Incorrectly importing/exporting methods/classes across modules/dll (compiler specific).
MSVS requires you to specify which symbols to export and import using
__declspec(dllexport)
and__declspec(dllimport)
.This dual functionality is usually obtained through the use of a macro:
The macro
THIS_MODULE
would only be defined in the module that exports the function. That way, the declaration:expands to
and tells the compiler to export the function, as the current module contains its definition. When including the declaration in a different module, it would expand to
and tells the compiler that the definition is in one of the libraries you linked against (also see 1)).
You can similary import/export classes:
对
WinMain@16
的未定义引用或类似的'unusual'main()
入口点引用(特别是对于 视觉工作室)。您可能错过了为您的实际 IDE 选择正确的项目类型。 IDE 可能希望将 Windows 应用程序项目绑定到此类入口点函数(如上面缺少的参考中所指定),而不是常用的
int main(int argc, char** argv);
签名。如果您的 IDE 支持普通控制台项目,您可能希望选择此项目类型,而不是 Windows 应用程序项目。
以下是案例1 和案例2从现实世界问题中更详细地处理。
undefined reference to
WinMain@16
or similar 'unusual'main()
entry point reference (especially for visual-studio).You may have missed to choose the right project type with your actual IDE. The IDE may want to bind e.g. Windows Application projects to such entry point function (as specified in the missing reference above), instead of the commonly used
int main(int argc, char** argv);
signature.If your IDE supports Plain Console Projects you might want to choose this project type, instead of a windows application project.
Here are case1 and case2 handled in more detail from a real world problem.
另外,如果您使用 3rd 方库,请确保您拥有正确的 32/64 位二进制文件
Also if you're using 3rd party libraries make sure you have the correct 32/64 bit binaries
Microsoft 提供了
#pragma
在链接时引用正确的库;除了库路径包括库的目录之外,这应该是库的全名。
Microsoft offers a
#pragma
to reference the correct library at link time;In addition to the library path including the directory of the library, this should be the full name of the library.
Visual Studio NuGet 包需要更新为新的工具集版本
我刚刚在尝试将 libpng 与 Visual Studio 2013 链接时遇到了这个问题。问题是包文件仅包含 Visual Studio 2010 和 2012 的库。
正确的解决方案是希望开发人员发布更新的包,然后升级,但它对我来说是通过在 VS2013 中进行额外设置,指向 VS2012 库文件来实现的。
我通过查找
packagename\build\native\packagename.targets
并在该文件内复制所有来编辑包(在解决方案目录内的
部分。我将仅条件字段中的packages
文件夹中) v110v110
更改为v120
,非常小心地将文件名路径全部保留为v110
。这只是允许 Visual Studio 2013 链接到 2012 年的库,在本例中,它起作用了。Visual Studio NuGet package needs to be updated for new toolset version
I just had this problem trying to link libpng with Visual Studio 2013. The problem is that the package file only had libraries for Visual Studio 2010 and 2012.
The correct solution is to hope the developer releases an updated package and then upgrade, but it worked for me by hacking in an extra setting for VS2013, pointing at the VS2012 library files.
I edited the package (in the
packages
folder inside the solution's directory) by findingpackagename\build\native\packagename.targets
and inside that file, copying all thev110
sections. I changed thev110
tov120
in the condition fields only being very careful to leave the filename paths all asv110
. This simply allowed Visual Studio 2013 to link to the libraries for 2012, and in this case, it worked.假设您有一个用 C++ 编写的大项目,其中有一千个 .cpp 文件和一千个 .h 文件。假设该项目还依赖于十个静态库。假设我们在 Windows 上,并在 Visual Studio 20xx 中构建我们的项目。当您按 Ctrl + F7 Visual Studio 开始编译整个解决方案时(假设我们解决方案中只有一个项目)
编译的含义是什么?
.cpp 文件中可能定义或未定义的符号
编译的第二步由 Linker 完成。Linker 应该合并所有目标文件并最终构建输出(可能是可执行文件或库)
链接项目的步骤
-如果链接器找不到您在一个 .cpp 中编写的符号,他会引发一个链接器时间错误,听起来可能像
错误 LNK2001:无法解析的外部符号“void __cdecl foo(void)”(?foo@@YAXXZ)
观察
如何解决此类错误
编译器时间错误:
链接器时间错误
#pragma Once
允许编译器不包含一个头文件(如果该头文件已包含在已编译的当前 .cpp 中)Suppose you have a big project written in c++ which has a thousand of .cpp files and a thousand of .h files.And let's says the project also depends on ten static libraries. Let's says we are on Windows and we build our project in Visual Studio 20xx. When you press Ctrl + F7 Visual Studio to start compiling the whole solution ( suppose we have just one project in the solution )
What's the meaning of compilation ?
that may or may not be defined in the file .cpp
The Second step of compilation is done by Linker.Linker should merge all the object file and build finally the output ( which may be an executable or a library)
Steps In Linking a project
-If the Linker could not find the symbol which you write in one .cpp he raises a linker time error which may sound like
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
Observation
How To Solve this kind of error
Compiler Time Error :
Linker Time Error
#pragma once
for allowing compiler not to include one header if it was already included in the current .cpp which are compiled使用链接器帮助诊断错误
大多数现代链接器都包含一个可以不同程度打印的详细选项;
对于 gcc 和 clang;您通常会在命令行中添加 -v -Wl,--verbose 或 -v -Wl,-v 。更多详细信息可以在这里找到;
对于 MSVC,
/VERBOSE
(特别是/VERBOSE:LIB
)被添加到链接命令行中。/VERBOSE
链接器选项。Use the linker to help diagnose the error
Most modern linkers include a verbose option that prints out to varying degrees;
For gcc and clang; you would typically add
-v -Wl,--verbose
or-v -Wl,-v
to the command line. More details can be found here;For MSVC,
/VERBOSE
(in particular/VERBOSE:LIB
) is added to the link command line./VERBOSE
linker option.编译器/IDE 中的错误
我最近遇到了这个问题,结果这是 Visual Studio Express 2013 中的一个错误。我必须从项目中删除源文件并重新添加它以克服该错误。
如果您认为这可能是编译器/IDE 中的错误,请尝试以下步骤:
通过删除目标文件手动完成)
从原始源代码复制所有源代码。
A bug in the compiler/IDE
I recently had this problem, and it turned out it was a bug in Visual Studio Express 2013. I had to remove a source file from the project and re-add it to overcome the bug.
Steps to try if you believe it could be a bug in compiler/IDE:
manually do it by deleting the object files)
copying all source code from the original one.
链接的 .lib 文件与 .dll 关联
我也遇到了同样的问题。假设我有项目 MyProject 和 TestProject。我已有效地将 MyProject 的 lib 文件链接到 TestProject。但是,该 lib 文件是在构建 MyProject 的 DLL 时生成的。另外,我没有包含 MyProject 中所有方法的源代码,而仅包含对 DLL 入口点的访问。
为了解决这个问题,我将 MyProject 构建为 LIB,并将 TestProject 链接到此 .lib 文件(我将生成的 .lib 文件复制粘贴到 TestProject 文件夹中)。然后我可以再次将 MyProject 构建为 DLL。它正在编译,因为 TestProject 链接到的库确实包含 MyProject 中类中的所有方法的代码。
Linked .lib file is associated to a .dll
I had the same issue. Say i have projects MyProject and TestProject. I had effectively linked the lib file for MyProject to the TestProject. However, this lib file was produced as the DLL for the MyProject was built. Also, I did not contain source code for all methods in the MyProject, but only access to the DLL's entry points.
To solve the issue, i built the MyProject as a LIB, and linked TestProject to this .lib file (i copy paste the generated .lib file into the TestProject folder). I can then build again MyProject as a DLL. It is compiling since the lib to which TestProject is linked does contain code for all methods in classes in MyProject.
由于人们在涉及链接器错误时似乎会被引导到这个问题,因此我将在此处添加此内容。
GCC 5.2.0 出现链接器错误的一个可能原因是现在默认选择新的 libstdc++ 库 ABI。
因此,如果您在 5.1.0 之后切换到 GCC 时突然出现链接器错误,则需要检查一下。
Since people seem to be directed to this question when it comes to linker errors I am going to add this here.
One possible reason for linker errors with GCC 5.2.0 is that a new libstdc++ library ABI is now chosen by default.
So if you suddenly get linker errors when switching to a GCC after 5.1.0 this would be a thing to check out.
您的链接在引用库的目标文件之前消耗库
libfoo
依赖于libbar
,则您的链接正确地将libfoo
放在libbar 之前
。未定义的引用
某事错误。#include
d 和 实际上是在您链接的库中定义的。示例是 C 语言。它们同样可以是 C++
涉及您自己构建的静态库的最小示例
my_lib.c
my_lib.h
eg1.c
构建静态库:
编译程序:
尝试将其与
libmy_lib.a
链接,但失败:如果一步编译和链接,结果相同,例如:
一个涉及 a 的最小示例共享系统库、压缩库
libz
eg2.c
编译您的程序:
尝试将您的程序与
libz
链接,但失败:如果您一次性编译和链接,则相同:
并且涉及
pkg-config
的示例 2 的变体:您做错了什么?
在您想要链接的目标文件和库的序列中,使您的
程序中,您将库放在引用的目标文件之前
他们。您需要将库放在引用的目标文件之后
给他们。
正确链接示例 1:
成功:
正确链接示例 2:
成功:
正确链接示例 2
pkg-config
变体:解释
从这里开始阅读是可选的。
默认情况下,GCC 在您的发行版上生成链接命令,
从左到右消耗链接中的文件
命令行序列。当它发现某个文件引用某物时
并且不包含它的定义,将搜索定义
在右侧的文件中。如果它最终找到一个定义,
参考已解决。如果最终仍有任何参考文献未解决,
链接失败:链接器不向后搜索。
首先,示例 1,使用静态库
my_lib.a
静态库是目标文件的索引存档。当链接器
在链接序列中找到
-lmy_lib
并找出 this 引用到静态库
./libmy_lib.a
,它想知道你的程序是否需要 libmy_lib.a 中的任何目标文件。
libmy_lib.a
中只有一个目标文件,即my_lib.o
,并且只定义了一个东西在
my_lib.o
中,即函数hw
。当且仅当链接器已经知道这一点时,它才会决定您的程序需要
my_lib.o
你的程序引用
hw
,它已经在一个或多个目标文件中添加到程序中,并且它没有添加任何目标文件
包含
hw
的定义。如果这是真的,那么链接器将从库中提取
my_lib.o
的副本,并将其添加到您的程序中。然后,您的程序包含
hw
的定义,因此它对
hw
的引用已解决。当您尝试链接程序时,例如:
链接器在看到时尚未添加
eg1.o
到程序-lmy_lib
。因为此时它还没有看到eg1.o
。您的程序尚未对
hw
进行任何引用:它尚未做出任何引用根本,因为它所做的所有引用
位于
eg1.o
中。因此链接器不会将
my_lib.o
添加到程序中,并且没有进一步的操作用于
libmy_lib.a
。接下来,它找到
eg1.o
,并将其添加到程序中。目标文件位于链接序列始终添加到程序中。现在,该程序使
对
hw
的引用,并且不包含hw
的定义;但连接序列中没有任何东西可以提供缺失的
定义。对
hw
的引用最终未解析,并且链接失败。其次,示例 2,使用共享库
libz
共享库不是目标文件或类似文件的存档。它是
更像是一个没有
main
函数的程序相反,公开它定义的多个其他符号,以便其他
程序可以在运行时使用它们。
如今,许多 Linux 发行版都配置其 GCC 工具链,以便其语言驱动程序(
gcc
、g++
、gfortran
等)指示系统链接器 (
ld
) 根据需要链接共享库。您已经获得了这些发行版之一。
这意味着当链接器在链接序列中找到
-lz
并发现 this 引用时到共享库(例如)
/usr/lib/x86_64-linux-gnu/libz.so
,它想知道它添加到程序中但尚未定义的任何引用是否具有由libz
导出的定义如果这是真的,那么链接器将不会从
libz
中复制任何块,并且将它们添加到您的程序中;相反,它只会修改你的程序代码
这样:-
在运行时,系统程序加载器会将
libz
的副本加载到每当加载程序的副本时,都会使用与您的程序相同的进程来运行它。
在运行时,每当您的程序引用在中定义的内容时
libz
,该引用使用由libz
的副本导出的定义相同的进程。
您的程序只想引用具有由
libz
导出的定义的事物,即函数
zlibVersion
,在eg2.c
中仅被引用一次。如果链接器将该引用添加到您的程序中,然后找到定义
由
libz
导出,引用已解析,但是当您尝试像这样链接程序时:
事件的顺序是错误的,就像示例 1 一样。
当链接器找到
-lz
时,没有对任何内容的引用程序中:都是在
eg2.o
中,还没见过。所以链接器认为它对
libz
没有用处。当它到达eg2.o
时,将其添加到程序中,然后对
zlibVersion
有未定义的引用,链接序列完成;该引用未解析,并且链接失败。
最后,示例 2 的
pkg-config
变体现在有了显而易见的解释。shell 扩展后:
变为:
这又是示例 2。
我可以重现示例 1 中的问题,但不能重现示例 2 中的问题
连接:
对您来说效果很好!
(或者:该链接在 Fedora 23 上运行良好,但在 Ubuntu 16.04 上失败)
这是因为该链接运行的发行版是
不配置其 GCC 工具链来根据需要链接共享库。
过去,类 UNIX 系统链接静态和共享是很正常的
图书馆按不同的规则。链接序列中的静态库被链接
在示例 1 中解释的按需基础上,但共享库是无条件链接的。
这种行为在链接时很经济,因为链接器不必考虑
程序是否需要共享库:如果是共享库,
链接它。大多数链接中的大多数库都是共享库。但也有缺点:-
在运行时是不经济的,因为它可能导致共享库
与程序一起加载,即使不需要它们。
静态库和共享库的不同链接规则可能会令人困惑
对于不熟练的程序员,他们可能不知道
-lfoo
是否在其链接中将解析为
/some/where/libfoo.a
或/some/where/libfoo.so
,并且可能不理解共享库和静态库之间的区别
无论如何。
这种权衡导致了今天的分裂局面。一些发行版有
更改了共享库的 GCC 链接规则,以便按需
原则适用于所有图书馆。一些发行版仍然沿用旧版本
方式。
为什么我同时编译和链接仍然遇到这个问题?
如果我这样做:
gcc 肯定必须首先编译
eg1.c
,然后链接生成的结果带有
libmy_lib.a
的目标文件。那么它怎么可能不知道目标文件进行链接时需要吗?
因为使用单个命令进行编译和链接不会改变
连接序列的顺序。
当您运行上面的命令时,
gcc
会发现您想要编译 +连锁。所以在幕后,它生成一个编译命令,并运行
它,然后生成一个链接命令,并运行它,就像您运行了
两个命令:
因此,链接会失败,就像您运行这两个命令时一样。这
您在失败中注意到的唯一区别是 gcc 生成了
编译+链接情况下的临时目标文件,因为你没有告诉它
使用
eg1.o
。我们看到:而不是:
另请参阅
指定相互依赖的链接库的顺序是错误的
将相互依赖的库按错误的顺序放置只是一种方式
您可以在其中获取需要即将到来的事物的定义的文件
链接中比提供定义的文件晚。将库放在前面
引用它们的目标文件是犯同样错误的另一种方式。
Your linkage consumes libraries before the object files that refer to them
libfoo
depends onlibbar
, then your linkage correctly putslibfoo
beforelibbar
.undefined reference to
something errors.#include
d and are in fact defined in the libraries that you are linking.Examples are in C. They could equally well be C++
A minimal example involving a static library you built yourself
my_lib.c
my_lib.h
eg1.c
You build your static library:
You compile your program:
You try to link it with
libmy_lib.a
and fail:The same result if you compile and link in one step, like:
A minimal example involving a shared system library, the compression library
libz
eg2.c
Compile your program:
Try to link your program with
libz
and fail:Same if you compile and link in one go:
And a variation on example 2 involving
pkg-config
:What are you doing wrong?
In the sequence of object files and libraries you want to link to make your
program, you are placing the libraries before the object files that refer to
them. You need to place the libraries after the object files that refer
to them.
Link example 1 correctly:
Success:
Link example 2 correctly:
Success:
Link the example 2
pkg-config
variation correctly:The explanation
Reading is optional from here on.
By default, a linkage command generated by GCC, on your distro,
consumes the files in the linkage from left to right in
commandline sequence. When it finds that a file refers to something
and does not contain a definition for it, to will search for a definition
in files further to the right. If it eventually finds a definition, the
reference is resolved. If any references remain unresolved at the end,
the linkage fails: the linker does not search backwards.
First, example 1, with static library
my_lib.a
A static library is an indexed archive of object files. When the linker
finds
-lmy_lib
in the linkage sequence and figures out that this refersto the static library
./libmy_lib.a
, it wants to know whether your programneeds any of the object files in
libmy_lib.a
.There is only object file in
libmy_lib.a
, namelymy_lib.o
, and there's only one thing definedin
my_lib.o
, namely the functionhw
.The linker will decide that your program needs
my_lib.o
if and only if it already knows thatyour program refers to
hw
, in one or more of the object files it has alreadyadded to the program, and that none of the object files it has already added
contains a definition for
hw
.If that is true, then the linker will extract a copy of
my_lib.o
from the library andadd it to your program. Then, your program contains a definition for
hw
, soits references to
hw
are resolved.When you try to link the program like:
the linker has not added
eg1.o
to the program when it sees-lmy_lib
. Because at that point, it has not seeneg1.o
.Your program does not yet make any references to
hw
: itdoes not yet make any references at all, because all the references it makes
are in
eg1.o
.So the linker does not add
my_lib.o
to the program and has no furtheruse for
libmy_lib.a
.Next, it finds
eg1.o
, and adds it to be program. An object file in thelinkage sequence is always added to the program. Now, the program makes
a reference to
hw
, and does not contain a definition ofhw
; butthere is nothing left in the linkage sequence that could provide the missing
definition. The reference to
hw
ends up unresolved, and the linkage fails.Second, example 2, with shared library
libz
A shared library isn't an archive of object files or anything like it. It's
much more like a program that doesn't have a
main
function andinstead exposes multiple other symbols that it defines, so that other
programs can use them at runtime.
Many Linux distros today configure their GCC toolchain so that its language drivers (
gcc
,g++
,gfortran
etc)instruct the system linker (
ld
) to link shared libraries on an as-needed basis.You have got one of those distros.
This means that when the linker finds
-lz
in the linkage sequence, and figures out that this refersto the shared library (say)
/usr/lib/x86_64-linux-gnu/libz.so
, it wants to know whether any references that it has added to your program that aren't yet defined have definitions that are exported bylibz
If that is true, then the linker will not copy any chunks out of
libz
andadd them to your program; instead, it will just doctor the code of your program
so that:-
At runtime, the system program loader will load a copy of
libz
into thesame process as your program whenever it loads a copy of your program, to run it.
At runtime, whenever your program refers to something that is defined in
libz
, that reference uses the definition exported by the copy oflibz
inthe same process.
Your program wants to refer to just one thing that has a definition exported by
libz
,namely the function
zlibVersion
, which is referred to just once, ineg2.c
.If the linker adds that reference to your program, and then finds the definition
exported by
libz
, the reference is resolvedBut when you try to link the program like:
the order of events is wrong in just the same way as with example 1.
At the point when the linker finds
-lz
, there are no references to anythingin the program: they are all in
eg2.o
, which has not yet been seen. So thelinker decides it has no use for
libz
. When it reacheseg2.o
, adds it to the program,and then has undefined reference to
zlibVersion
, the linkage sequence is finished;that reference is unresolved, and the linkage fails.
Lastly, the
pkg-config
variation of example 2 has a now obvious explanation.After shell-expansion:
becomes:
which is just example 2 again.
I can reproduce the problem in example 1, but not in example 2
The linkage:
works just fine for you!
(Or: That linkage worked fine for you on, say, Fedora 23, but fails on Ubuntu 16.04)
That's because the distro on which the linkage works is one of the ones that
does not configure its GCC toolchain to link shared libraries as-needed.
Back in the day, it was normal for unix-like systems to link static and shared
libraries by different rules. Static libraries in a linkage sequence were linked
on the as-needed basis explained in example 1, but shared libraries were linked unconditionally.
This behaviour is economical at linktime because the linker doesn't have to ponder
whether a shared library is needed by the program: if it's a shared library,
link it. And most libraries in most linkages are shared libraries. But there are disadvantages too:-
It is uneconomical at runtime, because it can cause shared libraries to be
loaded along with a program even if doesn't need them.
The different linkage rules for static and shared libraries can be confusing
to inexpert programmers, who may not know whether
-lfoo
in their linkageis going to resolve to
/some/where/libfoo.a
or to/some/where/libfoo.so
,and might not understand the difference between shared and static libraries
anyway.
This trade-off has led to the schismatic situation today. Some distros have
changed their GCC linkage rules for shared libraries so that the as-needed
principle applies for all libraries. Some distros have stuck with the old
way.
Why do I still get this problem even if I compile-and-link at the same time?
If I just do:
surely gcc has to compile
eg1.c
first, and then link the resultingobject file with
libmy_lib.a
. So how can it not know that object fileis needed when it's doing the linking?
Because compiling and linking with a single command does not change the
order of the linkage sequence.
When you run the command above,
gcc
figures out that you want compilation +linkage. So behind the scenes, it generates a compilation command, and runs
it, then generates a linkage command, and runs it, as if you had run the
two commands:
So the linkage fails just as it does if you do run those two commands. The
only difference you notice in the failure is that gcc has generated a
temporary object file in the compile + link case, because you're not telling it
to use
eg1.o
. We see:instead of:
See also
The order in which interdependent linked libraries are specified is wrong
Putting interdependent libraries in the wrong order is just one way
in which you can get files that need definitions of things coming
later in the linkage than the files that provide the definitions. Putting libraries before the
object files that refer to them is another way of making the same mistake.
不支持链接描述文件的 GNU ld 包装器
一些 .so 文件实际上是 GNU ld 链接器脚本,例如 libtbb.so 文件是一个包含以下内容的 ASCII 文本文件:
一些更复杂的版本可能不支持此功能。例如,如果在编译器选项中包含 -v,则可以看到 mainwin gcc 包装器 mwdip 丢弃要链接的库的详细输出列表中的链接器脚本命令文件。一个简单的解决方法是将链接器脚本输入命令文件替换为文件的副本(或符号链接),例如
或者您可以替换 -l 参数使用 .so 的完整路径,例如,代替
-ltbb
执行/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2< /代码>
A wrapper around GNU ld that doesn't support linker scripts
Some .so files are actually GNU ld linker scripts, e.g. libtbb.so file is an ASCII text file with this contents:
Some more complex builds may not support this. For example, if you include -v in the compiler options, you can see that the mainwin gcc wrapper mwdip discards linker script command files in the verbose output list of libraries to link in. A simple work around is to replace the linker script input command file with a copy of the file instead (or a symlink), e.g.
Or you could replace the -l argument with the full path of the .so, e.g. instead of
-ltbb
do/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
与模板交友...
给定带有友元运算符(或函数)的模板类型的代码片段;
运算符<<
被声明为非模板函数。对于与Foo
一起使用的每种类型T
,都需要有一个非模板化运算符<<
。例如,如果声明了一个Foo
类型,则必须有一个运算符实现,如下所示;由于它没有实现,链接器无法找到它并导致错误。
要纠正此问题,您可以在 Foo 类型之前声明一个模板运算符,然后将适当的实例化声明为友元。语法有点尴尬,但看起来如下;
上面的代码将操作符的友谊限制为
Foo
的相应实例化,即operator<<
实例化仅限于访问Foo
实例化的私有成员。替代方案包括:
允许友谊扩展到模板的所有实例,如下所示;
或者,
运算符<<
的实现可以在类定义中内联完成;注意,当运算符(或函数)的声明仅出现在类中时,该名称不可用于“正常”查找,只能用于参数相关查找,来自 cppreference;
有关模板好友的进一步阅读,请访问 cppreference 和<一href="https://isocpp.org/wiki/faq/templates#template-friends" rel="noreferrer">C++ 常见问题解答。
显示上述技术的代码列表。
作为失败代码示例的旁注; g++对此发出如下警告
Befriending templates...
Given the code snippet of a template type with a friend operator (or function);
The
operator<<
is being declared as a non-template function. For every typeT
used withFoo
, there needs to be a non-templatedoperator<<
. For example, if there is a typeFoo<int>
declared, then there must be an operator implementation as follows;Since it is not implemented, the linker fails to find it and results in the error.
To correct this, you can declare a template operator before the
Foo
type and then declare as a friend, the appropriate instantiation. The syntax is a little awkward, but is looks as follows;The above code limits the friendship of the operator to the corresponding instantiation of
Foo
, i.e. theoperator<< <int>
instantiation is limited to access the private members of the instantiation ofFoo<int>
.Alternatives include;
Allowing the friendship to extend to all instantiations of the templates, as follows;
Or, the implementation for the
operator<<
can be done inline inside the class definition;Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;
There is further reading on template friends at cppreference and the C++ FAQ.
Code listing showing the techniques above.
As a side note to the failing code sample; g++ warns about this as follows
当包含路径不同时,
当头文件及其关联的共享库(.lib 文件)不同步时,可能会发生链接器错误。让我解释一下。
链接器如何工作?链接器通过比较函数声明(在标头中声明)与其定义(在共享库中)来匹配它们的签名。如果链接器找不到完美匹配的函数定义,您可能会收到链接器错误。
即使声明和定义看起来匹配,是否仍然可能出现链接器错误?是的!它们在源代码中可能看起来相同,但这实际上取决于编译器看到的内容。本质上,您最终可能会遇到这样的情况:
请注意,尽管两个函数声明在源代码中看起来相同,但根据编译器的不同,它们实际上是不同的。
你可能会问,一个人怎么会陷入这样的境地呢? 当然包括路径!如果在编译共享库时,包含路径指向
header1.h
并且您最终在自己的程序中使用header2.h
,那么您将不得不抓挠头文件想知道发生了什么(双关语)。下面解释了在现实世界中如何发生这种情况的示例。
通过示例进一步阐述
我有两个项目:
graphics.lib
和main.exe
。这两个项目都依赖于common_math.h
。假设该库导出以下函数:然后您继续将该库包含在您自己的项目中。
繁荣!您收到链接器错误,并且不知道为什么会失败。原因是公共库使用相同的 include
common_math.h
的不同版本(我在示例中通过包含不同的路径使其变得明显,但它可能并不总是那么明显。也许编译器设置中的包含路径不同)。请注意,在此示例中,链接器会告诉您它找不到
draw()
,而实际上您知道它显然是由库导出的。您可能会花费几个小时摸不着头脑,想知道出了什么问题。问题是,链接器看到不同的签名,因为参数类型略有不同。在示例中,就编译器而言,vec3
在两个项目中都是不同的类型。发生这种情况是因为它们来自两个略有不同的包含文件(也许包含文件来自两个不同版本的库)。如果您使用的是 Visual Studio,那么调试链接器
DUMPBIN 是您的好朋友。我确信其他编译器也有其他类似的工具。
这个过程是这样的:
[1] 我所说的项目是指链接在一起以生成库或可执行文件的一组源文件。
编辑 1:重写第一部分以更容易理解。请在下面发表评论,让我知道是否还有其他问题需要修复。谢谢!
When your include paths are different
Linker errors can happen when a header file and its associated shared library (.lib file) go out of sync. Let me explain.
How do linkers work? The linker matches a function declaration (declared in the header) with its definition (in the shared library) by comparing their signatures. You can get a linker error if the linker doesn't find a function definition that matches perfectly.
Is it possible to still get a linker error even though the declaration and the definition seem to match? Yes! They might look the same in source code, but it really depends on what the compiler sees. Essentially you could end up with a situation like this:
Note how even though both the function declarations look identical in source code, but they are really different according to the compiler.
You might ask how one ends up in a situation like that? Include paths of course! If when compiling the shared library, the include path leads to
header1.h
and you end up usingheader2.h
in your own program, you'll be left scratching your header wondering what happened (pun intended).An example of how this can happen in the real world is explained below.
Further elaboration with an example
I have two projects:
graphics.lib
andmain.exe
. Both projects depend oncommon_math.h
. Suppose the library exports the following function:And then you go ahead and include the library in your own project.
Boom! You get a linker error and you have no idea why it's failing. The reason is that the common library uses different versions of the same include
common_math.h
(I have made it obvious here in the example by including a different path, but it might not always be so obvious. Maybe the include path is different in the compiler settings).Note in this example, the linker would tell you it couldn't find
draw()
, when in reality you know it obviously is being exported by the library. You could spend hours scratching your head wondering what went wrong. The thing is, the linker sees a different signature because the parameter types are slightly different. In the example,vec3
is a different type in both projects as far as the compiler is concerned. This could happen because they come from two slightly different include files (maybe the include files come from two different versions of the library).Debugging the linker
DUMPBIN is your friend, if you are using Visual Studio. I'm sure other compilers have other similar tools.
The process goes like this:
[1] By project I mean a set of source files that are linked together to produce either a library or an executable.
EDIT 1: Rewrote first section to be easier to understand. Please comment below to let me know if something else needs to be fixed. Thanks!
不一致的
UNICODE
定义Windows UNICODE 构建是使用
TCHAR
等定义为wchar_t
等构建的。当不使用UNICODE 定义为构建,
TCHAR
定义为char
等。这些UNICODE
和_UNICODE
定义影响所有 "T
"字符串类型;LPTSTR
、LPCTSTR
和它们的麋鹿。构建一个定义了
UNICODE
的库并尝试在未定义UNICODE
的项目中链接该库将导致链接器错误,因为的定义不匹配>TCHAR
;char
与wchar_t
。错误通常包括具有
char
或wchar_t
派生类型的函数值,这些可能包括std::basic_string<>
等。出色地。浏览代码中受影响的函数时,通常会引用TCHAR
或std::basic_string
等。这是一个明显的迹象,该代码最初旨在用于 UNICODE 和多字节字符(或“窄”)构建。要纠正此问题,请使用一致的
UNICODE
(和_UNICODE
)定义构建所有必需的库和项目。这可以用任何一个来完成;
<前><代码>#define UNICODE
#定义_UNICODE
或者在项目设置中;
<块引用>
项目属性>一般>项目默认值>字符集
或在命令行上;
<前><代码>/DUNICODE /D_UNICODE
如果不打算使用 UNICODE,则另一种方法也适用,请确保未设置定义,和/或在项目中使用多字符设置并一致应用。
不要忘记“发布”和“调试”版本之间也要保持一致。
Inconsistent
UNICODE
definitionsA Windows UNICODE build is built with
TCHAR
etc. being defined aswchar_t
etc. When not building withUNICODE
defined as build withTCHAR
defined aschar
etc. TheseUNICODE
and_UNICODE
defines affect all the "T
" string types;LPTSTR
,LPCTSTR
and their elk.Building one library with
UNICODE
defined and attempting to link it in a project whereUNICODE
is not defined will result in linker errors since there will be a mismatch in the definition ofTCHAR
;char
vs.wchar_t
.The error usually includes a function a value with a
char
orwchar_t
derived type, these could includestd::basic_string<>
etc. as well. When browsing through the affected function in the code, there will often be a reference toTCHAR
orstd::basic_string<TCHAR>
etc. This is a tell-tale sign that the code was originally intended for both a UNICODE and a Multi-Byte Character (or "narrow") build.To correct this, build all the required libraries and projects with a consistent definition of
UNICODE
(and_UNICODE
).This can be done with either;
Or in the project settings;
Or on the command line;
The alternative is applicable as well, if UNICODE is not intended to be used, make sure the defines are not set, and/or the multi-character setting is used in the projects and consistently applied.
Do not forget to be consistent between the "Release" and "Debug" builds as well.
const 变量声明/定义中缺少“extern”(仅限 C++)
对于来自 C 的人来说,C++ 中的全局 const 变量具有内部(或静态)可能会令人惊讶连锁。在 C 中,情况并非如此,因为所有全局变量都隐式地
extern
(即,当缺少static
关键字时)。示例:
正确的做法是使用头文件并将其包含在 file2.cpp 和 file1.cpp 中
或者,可以在 file1.cpp 中使用显式
const
变量>外部Missing "extern" in
const
variable declarations/definitions (C++ only)For people coming from C it might be a surprise that in C++ global
const
variables have internal (or static) linkage. In C this was not the case, as all global variables are implicitlyextern
(i.e. when thestatic
keyword is missing).Example:
correct would be to use a header file and include it in file2.cpp and file1.cpp
Alternatively one could declare the
const
variable in file1.cpp with explicitextern
清理和重建
构建的“清理”可以消除以前的构建、失败的构建、不完整的构建和其他构建系统相关的构建问题中可能留下的“死木”。
一般来说,IDE 或构建将包括某种形式的“干净”功能,但这可能未正确配置(例如在手动 makefile 中)或可能失败(例如中间或生成的二进制文件是只读的)。
一旦“clean”完成,请验证“clean”是否已成功并且所有生成的中间文件(例如自动生成文件)是否已成功删除。
这个过程可以被视为最后的手段,但通常是一个好的第一步;特别是如果最近添加了与错误相关的代码(在本地或从源存储库)。
Clean and rebuild
A "clean" of the build can remove the "dead wood" that may be left lying around from previous builds, failed builds, incomplete builds and other build system related build issues.
In general the IDE or build will include some form of "clean" function, but this may not be correctly configured (e.g. in a manual makefile) or may fail (e.g. the intermediate or resultant binaries are read-only).
Once the "clean" has completed, verify that the "clean" has succeeded and all the generated intermediate file (e.g. an automated makefile) have been successfully removed.
This process can be seen as a final resort, but is often a good first step; especially if the code related to the error has recently been added (either locally or from the source repository).
尽管这是一个相当老的问题,有多个可接受的答案,但我想分享如何解决模糊“未定义的引用”错误。
不同版本的库
我使用别名来引用
std::filesystem::path
:文件系统自 C++17 起就位于标准库中,但我的程序也需要在 C 中编译++14 所以我决定使用变量别名:假设我有三个文件:main.cpp、file.h、file.cpp:
请注意 main.cpp 和 file.h 中使用的不同的库。由于 main.cpp #include'd "file.h" 在 <filesystem> 之后,使用的文件系统版本是 C++17 版本。我曾经使用以下命令编译程序:
$
g++ -g -std=c++17 -c main.cpp
->将 main.cpp 编译为 main.o$
g++ -g -std=c++17 -c file.cpp
->将file.cpp和file.h编译为file.o$
g++ -g -std=c++17 -o 可执行文件 main.o file.o -lstdc++fs
->链接 main.o 和 file.o这样任何函数包含在 file.o 中并在 main.o 中使用,必需的
path_t
给出“未定义”引用”错误,因为 main.o 引用了std::filesystem::path
但 file.o 引用了std::filesystem::path
std::experimental::filesystem::path
。解决方案
要解决此问题,我只需要更改;在 file.h 中到 。
Even though this is a pretty old questions with multiple accepted answers, I'd like to share how to resolve an obscure "undefined reference to" error.
Different versions of libraries
I was using an alias to refer to
std::filesystem::path
: filesystem is in the standard library since C++17 but my program needed to also compile in C++14 so I decided to use a variable alias:Let's say I have three files: main.cpp, file.h, file.cpp:
Note the different libraries used in main.cpp and file.h. Since main.cpp #include'd "file.h" after <filesystem>, the version of filesystem used there was the C++17 one. I used to compile the program with the following commands:
$
g++ -g -std=c++17 -c main.cpp
-> compiles main.cpp to main.o$
g++ -g -std=c++17 -c file.cpp
-> compiles file.cpp and file.h to file.o$
g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> links main.o and file.oThis way any function contained in file.o and used in main.o that required
path_t
gave "undefined reference" errors because main.o referred tostd::filesystem::path
but file.o tostd::experimental::filesystem::path
.Resolution
To fix this I just needed to change <experimental::filesystem> in file.h to <filesystem>.
链接共享库时,请确保所使用的符号未被隐藏。
gcc 的默认行为是所有符号都是可见的。但是,当使用选项
-fvisibility=hidden
构建翻译单元时,只有标有__attribute__ ((visibility ("default")))
的函数/符号是外部的结果共享对象。您可以通过调用来检查您正在查找的符号是否是外部的:
隐藏/本地符号以小写符号类型的
nm
显示,例如t
而不是 `T对于代码段:您还可以使用
nm
和选项-C
来对名称进行解析(如果使用了 C++)。与 Windows-dll 类似,可以用定义来标记公共函数,例如
DLL_PUBLIC
定义为:大致对应于 Windows'/MSVC-version:
更多 有关可见性的信息可以在 gcc wiki 上找到。
当使用
-fvisibility=hidden
编译翻译单元时,生成的符号仍然具有外部链接(按nm
以大写符号类型显示),并且可以用于外部链接,而无需如果目标文件成为静态库的一部分,就会出现问题。仅当目标文件链接到共享库时,链接才变为本地链接。要查找目标文件中的哪些符号被隐藏,请运行:
When linking against shared libraries, make sure that the used symbols are not hidden.
The default behavior of gcc is that all symbols are visible. However, when the translation units are built with option
-fvisibility=hidden
, only functions/symbols marked with__attribute__ ((visibility ("default")))
are external in the resulting shared object.You can check whether the symbols your are looking for are external by invoking:
the hidden/local symbols are shown by
nm
with lowercase symbol type, for examplet
instead of `T for code-section:You can also use
nm
with the option-C
to demangle the names (if C++ was used).Similar to Windows-dlls, one would mark public functions with a define, for example
DLL_PUBLIC
defined as:Which roughly corresponds to Windows'/MSVC-version:
More information about visibility can be found on the gcc wiki.
When a translation unit is compiled with
-fvisibility=hidden
the resulting symbols have still external linkage (shown with upper case symbol type bynm
) and can be used for external linkage without problem if the object files become part of a static libraries. The linkage becomes local only when the object files are linked into a shared library.To find which symbols in an object file are hidden run: