Aru*_*run 14 c++ compiler-construction templates g++
我听说过C++模板上下文中的代码膨胀.我知道现代C++编译器并非如此.但是,我想构建一个例子并说服自己.
让我们说我们上课了
template< typename T, size_t N >
class Array {
public:
T * data();
private:
T elems_[ N ];
};
template< typename T, size_t N >
T * Array<T>::data() {
return elems_;
}
Run Code Online (Sandbox Code Playgroud)
而且,假设types.h包含
typedef Array< int, 100 > MyArray;
Run Code Online (Sandbox Code Playgroud)
x.cpp 包含
MyArray ArrayX;
Run Code Online (Sandbox Code Playgroud)
并y.cpp包含
MyArray ArrayY;
Run Code Online (Sandbox Code Playgroud)
现在,我如何可以验证的代码空间MyArray::data()是相同的两个ArrayX和ArrayY?
还有什么我应该知道并从这个(或其他类似的简单)例子中验证?如果有任何g ++特定提示,我也对此感兴趣.
PS:关于臃肿,我甚至担心最轻微的臃肿,因为我来自嵌入式环境.
另外:如果模板类被显式实例化,情况是否会发生变化?
Ter*_*fey 12
你问的是错误的问题 - 你的例子中的任何"膨胀"都与模板无关.(你的问题的答案,顺便说一句,是在两个模块中取成员函数的地址,你会发现它们是相同的)
您真正想要问的是,对于每个模板实例化,生成的可执行文件是否会线性增长?答案是否定的,链接器/优化器将做魔术.
编译创建一种类型的exe:
Array< int, 100 > MyArray;
Run Code Online (Sandbox Code Playgroud)
注意产生的exe大小.现在再做一次:
Array< int, 100 > MyArray;
Array< int, 99 > MyArray;
Run Code Online (Sandbox Code Playgroud)
等等,对于30个左右的不同版本,绘制生成的exe大小.如果模板像人们想象的那样糟糕,那么每个唯一模板实例化的exe大小将增加固定量.
Boo*_*jum 10
在这种特定情况下,如果您进行任何类型的优化,您会发现g ++将倾向于内联访问者.这就是有一些小的代码臃肿,但如果调用的开销会更少,这是有争议的.
但是,验证编译内容的一种简单方法是使用该nm工具.如果我用一个简单的编译代码main()行使ArrayX::data()和ArrayY::data(),然后用编译它-O0关掉内联,我可以运行nm -C看在可执行文件中的符号:
% nm -C test
0804a040 B ArrayX
0804a1e0 B ArrayY
08049f08 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
0804858c R _IO_stdin_used
w _Jv_RegisterClasses
080484c4 W Array<int, 100u>::data()
08049ef8 d __CTOR_END__
08049ef4 d __CTOR_LIST__
08049f00 D __DTOR_END__
...
Run Code Online (Sandbox Code Playgroud)
您将看到Array<int, 100u>::data()符号仅在最终可执行文件中出现一次,即使两个翻译单元中的每一个的目标文件都包含它自己的副本.(该nm工具也适用于目标文件.您可以使用它来检查它x.o,y.o每个都有一个副本Array<int, 100u>::data().)
如果nm没有提供足够的细节,您可能还会看一下该objdump工具.这很像nm,但是打开调试符号后,它甚至可以显示诸如使用混合源代码行反汇编输出可执行文件之类的内容.
模板与此无关.
考虑这个小程序:
啊:
class a {
int foo() { return 42; }
};
Run Code Online (Sandbox Code Playgroud)
b.cpp:
#include "a.h"
void b() {
a my_a;
my_a.foo();
}
Run Code Online (Sandbox Code Playgroud)
c.cpp:
#include "a.h"
void c() {
a my_a;
my_a.foo();
}
Run Code Online (Sandbox Code Playgroud)
没有模板,但你有完全相同的问题.在多个翻译单元中定义相同的功能.规则是相同的:在最终程序中只允许存在一个定义,否则编译器将无法确定调用哪个定义,否则指向同一函数的两个函数指针可能指向不同的地址.
模板代码膨胀的"问题"是不同的:如果你创建了很多不同的同一模板实例.例如,使用你的类,这个程序将冒一些代码膨胀的风险:
Array< int, 100 > i100;
Array< int, 99 > i99;
Array< long, 100 > l100;
Array< long, 99> l99;
i100.Data();
i99.Data();
l100.Data();
l99.Data();
Run Code Online (Sandbox Code Playgroud)
严格地说,编译器需要创建4个不同的Data函数实例,每个模板参数对应一个.实际上,只要生成的代码相同,一些(但不是全部)编译器就会尝试将它们合并在一起.(在这种情况下,组件在许多平台上生成Array< int, 100 >并且Array< long, 100 >将是相同的,并且该函数也不依赖于数组大小,因此99和100变体也应该生成相同的代码,因此聪明的编译器将合并实例化重新走到一起.
模板没有魔力.他们并没有神秘地"膨胀"你的代码.它们只是为您提供了一个工具,可以让您轻松地从同一模板中创建大量不同的类型.如果您实际使用所有这些类型,则必须为所有这些类型生成代码.与C++一样,您需要为使用的内容付费.如果你同时使用an Array<long, 100>,an Array<int, 100>,an Array<unsigned long, 100>和an Array<unsigned int, 100>,那么你会得到四个不同的类,因为你要求的是四个不同的类.如果你不要求四个不同的课程,他们不会花费你任何东西.
使用模板来更好地说明代码膨胀是使用模板来生成代码,而不是变量。典型的恐慌是由于编译器为模板(模板)的每个实例生成代码造成的。这类似于由于内联函数和方法而导致的代码膨胀。然而,现代编译器和链接器可以执行魔法来减少代码大小,具体取决于优化设置。
例如:
template <typename Any_Type>
void Print_Hello(const Any_Type& v)
{
std::cout << "Hello, your value is:\n"
<< v
<< "\n";
return;
}
Run Code Online (Sandbox Code Playgroud)
最好将上面的代码视为一个模板。编译器将根据传递给 的类型生成代码Print_Hello。这里的臃肿之处在于,实际上很少有代码依赖于该变量。 (可以通过分解 const 代码和数据来减少。)
令人担心的是,编译器将使用相同的变量类型为每个实例化生成代码,从而构建重复的代码:
int main(void)
{
int a = 5;
int b = 6;
Print_Hello(a); // Instantiation #1
Print_Hello(b); // Instantiation #2
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当模板(模板)在不同的翻译单元中实例化时,这种恐惧也可能会扩大。
现代编译器和链接器很智能。智能编译器会识别模板函数调用并转换为一些唯一的损坏名称。然后,编译器将为每次调用仅使用一个实例化。类似于函数重载。
即使编译器很草率并生成了该函数的多个实例(针对同一类型),链接器也会识别重复项并仅将一个实例放入可执行文件中。
如果使用不当,函数或方法模板可能会添加额外的代码。示例是大型函数,仅在少数区域中存在类型差异。它们的非类型化代码与类型相关代码的比例很高。
上面例子的一个实现,没有那么臃肿:
void Print_Prompt(void)
{
std::cout << "Hello, your value is:\n";
return;
}
template <typename Any_Type>
void Better_Print_Hello(const Any_Type& v)
{
Print_Prompt();
std::cout << v << "\n";
return;
}
Run Code Online (Sandbox Code Playgroud)
主要区别在于,不依赖于变量类型的代码已被分解为新函数。对于这个小例子来说,这似乎不值得,但它说明了这个概念。这个概念是将函数重构为依赖于变量类型和不依赖于变量类型的部分。依赖的部分被转换为模板化函数。
| 归档时间: |
|
| 查看次数: |
8220 次 |
| 最近记录: |