如何使 ld 将 Multiply 定义的结构/类视为错误?
Posted
技术标签:
【中文标题】如何使 ld 将 Multiply 定义的结构/类视为错误?【英文标题】:how to make ld treat Multiply defined structs/classes as an error? 【发布时间】:2012-05-11 11:04:42 【问题描述】:编辑——澄清我的问题的目标: 我浪费了很多时间来诊断我希望链接器报告的问题,这是由公认的糟糕的编程风格引起的,例如将一段代码从一个编译单元复制粘贴到另一个编译单元,然后对其进行修改。
我正在寻找一种在编译/链接时检测此问题的方法。
在此设置中:
啊.h
void foo();
A.cpp
struct A
int values[100];
A()
std::cout << __FILE__ << ": A::A()\n";
;
void foo()
A a;
main.cpp
#include "A.h"
struct A
double values[100];
A()
std::cout << __FILE__ << ": A::A()\n";
;
int main() foo();
// void foo() ===> this would cause a linker error
我希望链接器报告结构 A
,或至少构造函数 A::A()
,被定义了两次。
但是,g++ 4.4 链接就好了。运行代码显示,在这种情况下,链接器选择使用来自 A.cpp 的A
。
$ g++ -Wall A.cpp main.cpp && ./a.out
A.cpp:3
A.cpp:7
A.cpp:3
当 函数 foo()
出现在两个目标文件中时,链接器会报告多重定义,但对于结构,它不会。
编辑:刚刚通过使用 nm -C *.o
发现 A.o 和 main.o 都将 A::A()
定义为 弱符号。这使得它可以从具有相同名称的符号池中“选择”。也许问题可以改写为“我怎样才能使编译器生成 strong 符号?”...
00000000 W A::A()
我怎样才能发现这个问题?
【问题讨论】:
像你刚才所做的那样在类中定义一个成员会隐式声明该成员为inline
,这样实现将允许你在整个程序中提供多个该成员的定义。仍然要求所有这些定义彼此足够相似(我认为由于使用了__FILE__
而不是这种情况),但这更难诊断(并且不是必需的)。
@LucDanton: 足够相似是完全一样,否则你就违反了ODR。
@LucDanton:我明白你的意思,但在这种情况下,nm
输出清楚地表明所述函数没有被内联,不是吗?
@xtofl 不要错误地认为inline
关键字与内联有任何关系(不应该责怪任何人)。它与 ODR 相关——在这里,通过使用 inline
(虽然是隐含的),您要求的是 ODR 的特殊版本,这使得诊断更加困难。由于您想要更多诊断,请删除inline
——加上将定义与声明分开通常也是更好的风格。
@LucDanton:不要让标准混淆你,没有比标准提供的更严格的完全相同定义:代码必须是同样,代码引用的对象必须是完全相同的对象,除非不可能,在这种情况下不能使用对象的标识,只能使用值,并且值必须完全相同。如果没有这个例外,这将违反 ODR:const int k = 10; inline int f() return k;
,因为常量具有内部链接,因此包含它的每个 TU 都有不同的k
。
【参考方案1】:
也许问题可以改写为“我怎样才能使编译器生成强符号?”...
尽量限制inline
函数的使用:
struct A
A();
;
// Inside A.cpp
A::A()
std::cout << __FILE__ << ": A::A()\n";
实现更有可能报告未声明 inline
的函数的 ODR 违规(包括隐式声明 inline
的函数,例如在类定义中定义的成员) ,尽管严格来说从来不需要这样的诊断。
【讨论】:
【参考方案2】:这不是问题,也不是重新定义。这就是 C++ 的工作原理。想想看——你把类 definitions 放在头文件中(只暴露声明远不常见)。标题几乎被复制粘贴到每个使用它们的翻译单元中。 不能在多个 TU 中拥有同一类的多个定义是错误的。所以,这不是要解决的问题。
但是,如果在同一个名称下定义了不同的类,编译器/链接器应该会报错。
【讨论】:
...当然,后者是问题真正造成伤害的时候。在过去的十年中,我已经花费了几个小时,遇到了分段错误(最佳情况)和由该问题引起的内存损坏。因此提出了这个问题。以上是关于如何使 ld 将 Multiply 定义的结构/类视为错误?的主要内容,如果未能解决你的问题,请参考以下文章
如何在需要等待内容加载的动态页面上使用 JSON-LD 添加结构化数据?