链接错误:符号重复

Link Error: Duplicate Symbol

本文关键字:符号 错误 链接      更新时间:2023-10-16

下面列出了 4 个源文件:

//a.h
#pragma once
namespace proj {
class A {} a;
} // namespace proj
<小时 />
//b.h
#pragma once
namespace proj {
int foo();
} // namespace proj
<小时 />
// b.cpp
#include "proj/a.h"
namespace proj {
int foo() {
A b = a;
return 0;
}
} // namespace proj
<小时 />
// c.cpp
#include "proj/a.h"
#include "proj/b.h"
using namespace proj;
int main() {
A b = a;
foo();
return 0;
}

当我尝试编译c.cpp时,出现以下链接错误:

duplicate symbol proj::a      in:
buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
duplicate symbol ___odr_asan._ZN4proj1aE in:
buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
ld: 2 duplicate symbols for architecture x86_64
collect2: error: ld returned 1 exit status
Build failed: Command failed with exit code 1.
stderr: duplicate symbol proj::a      in:
buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
duplicate symbol ___odr_asan._ZN4proj1aE in:
buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
ld: 2 duplicate symbols for architecture x86_64
collect2: error: ld returned 1 exit status

我认为发生这种情况是因为 b.cpp 是独立于 c.cpp 编译的,因此预处理器在每个文件中单独包含标头 a.h,当需要链接时,链接器会找到符号 a 的两个版本。

如何声明一个类的实例(在本例中为a个)可用于我的整个程序并避免上述链接错误?

参考

我在带有-std=c++17的Mac OS X 10.13.3上使用gcc-7(gcc-7 (Homebrew GCC 7.2.0_1) 7.2.0)。

构建系统

这应该与问题无关,但无论如何都包括在内,以防有人发现它有帮助。

我正在使用buck使用以下BUCK文件编译代码(尽管这应该是无关紧要的):

cxx_library(
name='a',
exported_headers=['a.h'],
visibility=['PUBLIC'],
)
cxx_library(
name='b',
exported_headers=['b.h'],
srcs = ['b.cpp'],
deps = [':a'],
visibility=['PUBLIC'],
)
cxx_binary(
name='c',
srcs = ['c.cpp'],
deps = [':a', ':b'],
)

由于这被标记为 C++17,因此您可以利用新的内联变量语言功能:

namespace proj {
class A {};
inline A a;
} // namespace proj

inline变量现在的行为方式与inline函数相同:a的多个定义被折叠为一个。

对于c++17之前的标准发布解决方案:1

链接器错误的原因很明显

//a.h
#pragma once
namespace proj {
class A {} a; // Declares proj::A proj::a implicitly as an instance
// everywhere a.h is included.
// Thus the linker gets confused which one to use primarly.
}

备选方案1(一个实例):

如何声明一个可在整个程序中使用的类实例(在本例中为 a)并避免上述链接错误?

// a.h
#pragma once
namespace proj {
class A {};
extern A a;
}

// a.cpp
#include "a.h"
namespace proj {
proj:A a;
}

备选方案 2(每个翻译单元一个实例):

// a.h
#pragma once
namespace proj {
class A {};
}

// b.cpp   
#include "a.h"
namespace { // <<< unnamed (aka anonymous) namespace
//     privately visible for translation unit
proj:A a;
}

// c.cpp   
#include "a.h"
namespace {
proj:A a;
}

1)否则,@Barry的答案可能最适用。

相关文章: