gpt4 book ai didi

c++ - 编译 typeid(obj) 时会发生什么 - C++

转载 作者:太空狗 更新时间:2023-10-29 22:54:35 24 4
gpt4 key购买 nike

我的程序中有一个示例类,如下所示

template<class T>
class MyTemplate1
{
public:
T a;

MyTemplate1(T other){
a = other;
}
};

在我的主程序中,如果我只创建 MyTemplate1<int> 类型的对象,它没有在 readelf 输出中显示任何类型信息对象。但是如果我添加如下代码

MyTemplate1<int> obj = 12;
if(typeid(obj) == typeid(MyTemplate1<float>))
//some code

readelf 输出显示 MyTemplate1<int> 的类型信息和 MyTemplate1<float> 的类型信息.

$readelf -s -W <objfile> | findstr -I "MyTemplate"
9023: 00000000 8 OBJECT WEAK DEFAULT 2899 _ZTI11MyTemplate1IfE
9024: 00000000 8 OBJECT WEAK DEFAULT 2894 _ZTI11MyTemplate1IiE

有人可以解释一下这些 OBJECT 对应的是什么吗?这些 std::type_info 的全局实例是 MyTemplate1 类的吗?幕后到底发生了什么?

最佳答案

你不需要构造任何实例化的对象MyTemplate1<T>在编译单元中查看描述实例化类的类型信息对象该模板在目标文件的全局符号表中。你只需要引用typeid属于这样一类:-

$ cat main.cpp
#include <typeinfo>

template<class T>
class MyTemplate1
{
public:
T a;

MyTemplate1(T other){
a = other;
}
};

int main(void)
{
return (typeid(MyTemplate1<int>) == typeid(MyTemplate1<float>));
}

$ clang++ -Wall -c main.cpp
$ readelf -s -W main.o | grep MyTemplate1
5: 0000000000000000 16 OBJECT WEAK DEFAULT 15 _ZTI11MyTemplate1IfE
6: 0000000000000000 16 OBJECT WEAK DEFAULT 10 _ZTI11MyTemplate1IiE
7: 0000000000000000 17 OBJECT WEAK DEFAULT 13 _ZTS11MyTemplate1IfE
8: 0000000000000000 17 OBJECT WEAK DEFAULT 8 _ZTS11MyTemplate1IiE

$ c++filt _ZTI11MyTemplate1IfE
typeinfo for MyTemplate1<float>
$ c++filt _ZTI11MyTemplate1IiE
typeinfo for MyTemplate1<int>
$ c++filt _ZTS11MyTemplate1IfE
typeinfo name for MyTemplate1<float>
$ c++filt _ZTS11MyTemplate1IiE
typeinfo name for MyTemplate1<int>

这些 typeinfo对象在那里,因为正如@Peter 评论的那样,C++ 标准要求 typeid refers to an object of static storage duration

What exactly is happening under the hood?

您可能想知道:为什么编译器会生成这些 typeinfo对象符号 而不是简单的全局符号?为什么它在目标文件的不同部分定义它们? (我的目标文件的第 10 和 15 节,您的第 2894 和 2899 节)。

如果我们检查这些部分中的else:

$ readelf -s main.o | egrep '(10 |15 )'
5: 0000000000000000 16 OBJECT WEAK DEFAULT 15 _ZTI11MyTemplate1IfE
6: 0000000000000000 16 OBJECT WEAK DEFAULT 10 _ZTI11MyTemplate1IiE

我们看到每个对象都是其部分中的唯一。为什么会这样?

在我的 main.o ,第 10 和 15 部分是:

$ readelf -t main.o | egrep '(\[10\]|\[15\])'
[10] .rodata._ZTI11MyTemplate1IiE
[15] .rodata._ZTI11MyTemplate1IfE

其中每一个都是只读的数据部分,其意义在于:

__attribute__((section(.rodata._ZTI11MyTemplate1IiE)))
__attribute__((section(.rodata._ZTI11MyTemplate1IfE)))

只包含它之后的对象的定义命名。

编译器给每个对象一个数据段给自己同样的原因,它使符号 WEAK .引用 typeid(MyTemplate1<X>) , 对于任意类型 X , 可能是在#include 的同一链接内的多个翻译单元的定义 MyTemplate1 .为了避免在这种情况下出现多重定义错误的链接失败,编译器使符号变弱。链接器将容忍 weak 符号的多个定义,解析所有仅引用第一个出现的定义并忽略其余的部分。通过将一个独特的数据部分(或功能部分,视情况而定)用于每个弱模板实例化符号的定义,编译器赋予链接器自由丢弃定义相同弱符号的多余数据或函数部分对程序造成附带损害的风险。见:

$ cat MyTemplate1.hpp
#pragma once

template<class T>
class MyTemplate1
{
public:
T a;

MyTemplate1(T other){
a = other;
}
};

$ cat foo.cpp
#include "MyTemplate1.hpp"
#include <typeinfo>

int foo()
{
return typeid(MyTemplate1<int>) == typeid(MyTemplate1<float>);
}

$ cat bar.cpp
#include "MyTemplate1.hpp"
#include <typeinfo>

int bar()
{
return typeid(MyTemplate1<int>) != typeid(MyTemplate1<float>);
}

$ cat prog.cpp
extern int foo();
extern int bar();

int main()
{
return foo() && bar();
}

如果我们编译:

$ clang++ -Wall -c prog.cpp foo.cpp bar.cpp

并像这样链接(带有一些诊断信息):

$ clang++ -o prog prog.o bar.o foo.o \
-Wl,-trace-symbol=_ZTI11MyTemplate1IfE \
-Wl,-trace-symbol=_ZTI11MyTemplate1IiE \
-Wl,-Map=mapfile
/usr/bin/ld: bar.o: definition of _ZTI11MyTemplate1IfE
/usr/bin/ld: bar.o: definition of _ZTI11MyTemplate1IiE
/usr/bin/ld: foo.o: reference to _ZTI11MyTemplate1IfE
/usr/bin/ld: foo.o: reference to _ZTI11MyTemplate1IiE

正在输入 bar.o之前foo.o , 然后链接器选择的定义 _ZTI11MyTemplate1I(f|i)E来自 bar.o并忽略 foo.o 中的定义,解析 foo.o 中的引用 bar.o 中的定义. map 文件显示:

map 文件(一)

...
Discarded input sections
...
.rodata._ZTI11MyTemplate1IiE
0x0000000000000000 0x10 foo.o
...
.rodata._ZTI11MyTemplate1IfE
0x0000000000000000 0x10 foo.o
...

foo.o 中的定义被扔掉了。如果我们重新链接bar.o的顺序和 foo.o反转:

$ clang++ -o prog prog.o foo.o bar.o \
-Wl,-trace-symbol=_ZTI11MyTemplate1IfE \
-Wl,-trace-symbol=_ZTI11MyTemplate1IiE \
-Wl,-Map=mapfile
/usr/bin/ld: foo.o: definition of _ZTI11MyTemplate1IfE
/usr/bin/ld: foo.o: definition of _ZTI11MyTemplate1IiE
/usr/bin/ld: bar.o: reference to _ZTI11MyTemplate1IfE
/usr/bin/ld: bar.o: reference to _ZTI11MyTemplate1IiE

然后我们得到相反的结果。来自 foo.o 的定义有关联和:

map 文件(2)

...
Discarded input sections
...
.rodata._ZTI11MyTemplate1IiE
0x0000000000000000 0x10 bar.o
...
.rodata._ZTI11MyTemplate1IfE
0x0000000000000000 0x10 bar.o
...

bar.o 中的那些被扔掉。这个先到先得链接器的原理很好,因为 - 因为 - template<class T> MyTemplate1 的定义编译器发现在翻译单元foo.cpp与它在 bar.cpp 中找到的相同 , 一种C++ 标准要求 的条件,在 the One Definition Rule 中,但是 C++ 编译器无法执行

您可以对一般的模板实例化符号进行基本相同的观察,并且您在 clang++ 中看到的与在 g++ 中看到的基本相同。

关于c++ - 编译 typeid(obj) 时会发生什么 - C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55009569/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com