【问题标题】:Why one definition rule (ODR) is not honoured by g++ in this case ? .为什么在这种情况下 g++ 不遵守一个定义规则(ODR)? .
【发布时间】:2014-07-30 16:22:16
【问题描述】:

如您所见,链接时有多个 testfn 符号定义,但链接器采用第一次出现(按链接顺序)并忽略其他库中的其他出现。 这可能是链接器的工作方式。

但是有没有办法强制链接器在看到不同链接库中的多个符号时标记错误?或任何其他选项来捕获此类重复定义?


test1.h 的内容:

#ifndef TEST1
#define TEST1
void testfn();
#endif

test1.cpp 的内容

#include "test1.h"
#include <iostream>
using namespace std;
void testfn()
{
cout << "file1" << endl;
}

test.h 的内容:

#ifndef TEST
#define TEST
void testfn();
#endif

test.cpp 的内容:

#include "test.h"
#include <iostream>
using namespace std;
void testfn()
{
cout << "file" << endl;
}

main.cpp 的内容:

#include "test.h"
#include "test1.h"

int main()
{
testfn();
return 0;

}

创建了一个共享库。

 g++ -fPIC -shared libtest1.so test1.cpp
 g++ -fPIC -shared libtest.so test.cpp

使用库顺序#1 创建可执行文件

g++ -o main main.cpp -ltest -ltest1

使用库顺序#2 创建可执行文件

g++ -o main1 main.cpp -ltest1 -ltest

主输出

./main
file

main1 的输出

 ./main1
file1

【问题讨论】:

  • 我记得,违反 ODR 不需要诊断。

标签: c++ header linker g++ redefinition


【解决方案1】:

首先,它通过仅从库中取出一种方法来尊重 ODR。这就是共享库的工作方式。

如果您想看到编译器对此抱怨,请将它们全部链接在一起,无需库步骤。

g++ -o main1 main.cpp test1.cpp test.cpp

所以你的问题变成了,“我如何判断我是否有两个库都包含同名的标识符?”请记住,这通常不是问题,有时是故意的。我建议运行库工具(我不熟悉 g++ 工具集)以获取库列表,并运行它们的 DIFF。

【讨论】:

  • 我不这么认为——“定义可以在程序中显式出现,可以在标准或用户定义的库中找到”。我可能错了。
  • @LuchianGrigore :这涵盖了它的“不能定义一次”部分。但是,一旦链接器找到了可以满足的东西,就不需要继续寻找了。
  • 我同意,所以没有错误,但它仍然违反规则。
  • @LuchianGrigore:代码违反了规则,而不是编译器。
  • 同意,但我的观点是如何使用工具(编译器)捕捉此类问题?
【解决方案2】:

我看不到让 GNU ld 抱怨跨共享库的多个符号定义的方法。但是,如果涉及到普通的静态库存档,您也许可以使用--whole-archive/--no-whole-archive 选项集来获得您想要的:

例如,在构建 libtest.alibtest1.a 而不是共享库版本之后,我使用以下链接命令不会出错:

$ g++  -o main main.cpp   -ltest1 -ltest  -L.
$ ./main
file1

$ g++  -o main main.cpp   -ltest -ltest1  -L.
$ ./main
file

但确实会出现以下错误:

$ g++  -o main main.cpp -Wl,--whole-archive -ltest1 -ltest -Wl,--no-whole-archive -L.
./libtest.a(test.o): In function `testfn()':
test.cpp:(.text+0x0): multiple definition of `testfn()'
./libtest1.a(test1.o):test1.cpp:(.text+0x0): first defined here
collect2: ld returned 1 exit status

我不确定您是否希望将 --whole-archive 用于发布版本 - 可能只是作为可能的名称冲突的健全性检查(我认为使用 --whole-archive 通常会无缘无故地增加二进制文件的大小)。

另外,如前所述,这似乎对共享库没有影响。

【讨论】:

    猜你喜欢
    • 2020-05-16
    • 1970-01-01
    • 1970-01-01
    • 2016-10-19
    • 2022-12-01
    • 1970-01-01
    • 2017-01-07
    • 2020-04-21
    • 1970-01-01
    相关资源
    最近更新 更多