什么是未定义的参考/未解析的外部符号错误?常见原因是什么?如何解决/预防?
当前回答
指定相互依赖的链接库的顺序是错误的。
如果库相互依赖,则库的链接顺序也很重要。通常,如果库A依赖于库B,那么在链接器标志中,libA必须出现在libB之前。
例如:
// B.h
#ifndef B_H
#define B_H
struct B {
B(int);
int x;
};
#endif
// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}
// A.h
#include "B.h"
struct A {
A(int x);
B b;
};
// A.cpp
#include "A.h"
A::A(int x) : b(x) {}
// main.cpp
#include "A.h"
int main() {
A a(5);
return 0;
};
创建库:
$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o
ar: creating libB.a
a - B.o
编译:
$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out
再说一遍,顺序很重要!
其他回答
在链接共享库时,请确保未隐藏使用的符号。
gcc的默认行为是所有符号都可见。但是,当使用选项-fvisibility=hidden构建转换单元时,只有标记为__attribute__((可见性(“默认”))的函数/符号在生成的共享对象中是外部的。
您可以通过调用以下命令来检查要查找的符号是否为外部符号:
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
隐藏/本地符号用小写符号类型的nm表示,例如t而不是代码段的“t”:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
您还可以使用nm和选项-C来定义名称(如果使用了C++)。
与Windows DLL类似,可以使用define标记公共函数,例如DLL_public定义为:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
大致对应于Windows的/MSVC版本:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
有关可见性的更多信息可以在gcc wiki上找到。
当使用-fvisibility=hidden编译翻译单元时,生成的符号仍然具有外部链接(以大写符号类型显示,单位为nm),如果对象文件成为静态库的一部分,则可以毫无问题地用于外部链接。只有当对象文件链接到共享库中时,链接才会变为本地链接。
要查找对象文件中隐藏的符号,请运行:
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2
正在添加模板。。。
给定带有友元运算符(或函数)的模板类型的代码片段;
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
运算符<<被声明为非模板函数。对于与Foo一起使用的每种类型T,都需要有一个非模板运算符<<。例如,如果声明了一个类型Foo<int>,那么必须有如下的运算符实现:;
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
由于它未实现,链接器无法找到它并导致错误。
要更正此问题,可以在Foo类型之前声明一个模板运算符,然后将适当的实例化声明为友元。语法有点尴尬,但看起来如下:;
// forward declare the Foo
template <typename>
class Foo;
// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&);
template <typename T>
class Foo {
friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
// note the required <> ^^^^
// ...
};
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
// ... implement the operator
}
上述代码将运算符的友谊限制为Foo的相应实例化,即运算符<<<int>实例化被限制为访问Foo<int>的实例化的私有成员。
备选方案包括:;
允许友谊扩展到模板的所有实例化,如下所示;模板<typename T>Foo类{模板<typename T1>朋友std::ostream&operator<<(std::estream&os,const Foo<T1>/a);// ...};或者,运算符<<的实现可以在类定义内内联完成;模板<typename T>Foo类{朋友std::ostream&operator<<(std::estream&os,const Foo&a){ /*...*/ }// ...};
注意,当运算符(或函数)的声明仅出现在类中时,该名称不适用于“普通”查找,仅适用于cppreference中的依赖于参数的查找;
首先在类或类模板X中的友元声明中声明的名称将成为X最内部封闭命名空间的成员,但除非在命名空间范围内提供了匹配声明,否则无法进行查找(考虑X的依赖于参数的查找除外)。。。
cppreference和C++常见问题解答中有关于模板朋友的进一步阅读。
显示上述技术的代码列表。
作为失败代码示例的旁注;g++警告如下
警告:friend声明“std::ostream&operator<<(…)”声明非模板函数[-Wnon-template friend]注意:(如果这不是您想要的,请确保函数模板已经声明,并在此处的函数名称后面添加<>)
此外,如果您正在使用第三方库,请确保您拥有正确的32/64位二进制文件
指定相互依赖的链接库的顺序是错误的。
如果库相互依赖,则库的链接顺序也很重要。通常,如果库A依赖于库B,那么在链接器标志中,libA必须出现在libB之前。
例如:
// B.h
#ifndef B_H
#define B_H
struct B {
B(int);
int x;
};
#endif
// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}
// A.h
#include "B.h"
struct A {
A(int x);
B b;
};
// A.cpp
#include "A.h"
A::A(int x) : b(x) {}
// main.cpp
#include "A.h"
int main() {
A a(5);
return 0;
};
创建库:
$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o
ar: creating libB.a
a - B.o
编译:
$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out
再说一遍,顺序很重要!
因为当涉及到链接器错误时,人们似乎都在关注这个问题,所以我将在这里添加这个问题。
GCC 5.2.0中出现链接器错误的一个可能原因是现在默认选择了新的libstdc++库ABI。
如果您在std::__cxx11命名空间或标记[abi:cx11]中获得了有关符号未定义引用的链接器错误,则可能表明您正在尝试将使用_GLIBCXX_USE_cxx11_abi宏的不同值编译的对象文件链接在一起。这通常发生在链接到使用旧版本GCC编译的第三方库时。如果无法使用新的ABI重建第三方库,则需要使用旧的ABI重新编译代码。
因此,如果在5.1.0之后切换到GCC时突然出现链接器错误,这将是一件值得检查的事情。
推荐文章
- 如何在Go中使用c++
- 自定义c++分配器的引人注目的例子?
- RAII和c++中的智能指针
- 如何构建和使用谷歌TensorFlow c++ api
- 断言是邪恶的吗?
- 下面这些短语在c++中是什么意思:0 -,default-和value-initialization?
- 在STL地图中,使用map::insert比[]更好吗?
- C++ Linux的想法?
- 如何为Fedora安装g++ ?
- Std::cin输入空格?
- c++标准是否要求iostreams的性能很差,或者我只是在处理一个糟糕的实现?
- gcc在哪里查找C和c++头文件?
- 为什么我们需要require require ?
- 解析c++中的命令行参数?
- 我如何在c++中创建一个随机的字母数字字符串?