g++模板实例化器有多聪明(避免模板膨胀)
How smart is the g++ template instantiator (avoiding template bloat)
如果我有一个模板,里面有很多其他代码。g++会为每个版本的模板重新生成相同的代码吗?
例如:
template <typename> T
T parseSomething(const std::string& data) {
// Some state variables go here
enum State {state1,state2,state3} state;
for(std::string::const_iterator i=data.begin();i!=data.end();++i) {
// Some big testy stuff to see if we got in the right place
switch (state) {
case state1: {
switch (*i) {
case f: // ...
// ... lots of switchy stuff here ..
return T(*i);
}
}
所以在这个函数中。。唯一真正需要模板的是返回T(*i)行。
假设我用4个不同的Ts实例化它,例如
parseSomething<float>(data);
parseSomething<int>(data);
等等。
g++会为每个T生成所有其他代码(循环和开关部分)一个单独的时间吗?
或者只生成一次开关和循环是否足够聪明。。则对于每个T。。生成返回T(*i);线
我尝试过测试,使用-O0时,它肯定会在任何地方复制开关,但使用-O2及以上时,很难说;它看起来更聪明。。但它太聪明了,我无法破译ASM:)
这是我尝试用来测试的示例程序。
编译:
g++ -std=c++0x -fverbose-asm -ggdb3 -fvar-tracking-assignments -O6 -march=native codegen.cpp
要运行:
gdb --args ./a.out asdf1111
偏执代码版本:
#include <iostream>
#include <string>
using namespace std;
char getSomething(const string& myString) {
for(auto myPlase=myString.begin();myPlase!=myString.end();++myPlase) {
if (*myPlase == 'f') {
return *(myPlase+1);
}
}
}
template <typename T>
T getSomething(const string& myString) {
return T(getSomething(myString));
}
int main(int argc, char** argv) {
string base = argv[1];
float myFloat = getSomething<float>(base);
int myInt = getSomething<int>(base);
char myChar = getSomething<char>(base);
//string newString = getSomething<string>(base);
cout << myFloat << " " << myInt << " " << myChar << endl;
}
我想使用的代码版本:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
T getSomething(const string& myString) {
for(auto myPlace=myString.begin();myPlace!=myString.end();++myPlace) {
if (*myPlace == 'f') {
return T(*(myPlace+1));
}
}
}
int main(int argc, char** argv) {
string base = argv[1];
float myFloat = getSomething<float>(base);
int myInt = getSomething<int>(base);
char myChar = getSomething<char>(base);
//string newString = getSomething<string>(base);
cout << myFloat << " " << myInt << " " << myChar << endl;
}
我认为编译器不够聪明,无法合并独立于模板参数的代码。换句话说,函数将被实例化4次,每个T.一次
在linux上,可以在生成的对象文件上使用readelf -s
来转储公共符号,使用readelf -S
来转储节;每个非内联的非静态函数在符号表中都有一个(损坏的)条目。AFAIK,每个模板安装都有自己的部分,这样它们就可以在链接时合并。
无论是类还是函数,在模板中有一大块非参数化代码都是非常罕见的。
检测一大块不依赖于参数的代码听起来并不困难。引擎只需要记录相关AST节点中子树大小的一些度量,以及是否有任何子节点是模板参数。
但您建议的优化本质上需要将内部作用域与外部作用域分离,这意味着将它们重构为一个新函数。如果您有一个命名变量,而不是一个临时变量,其生存期包括内部switch
,该怎么办?堆栈将被重新排列,内部作用域将依赖于变量,尽管可能没有引用它,并且局部变量必须作为引用参数传递给switch
。这将是一个脆弱而复杂的优化。
如果模板膨胀是一个问题,我会认真推荐"偏执"版本,它将模板问题分离为一个包装函数。这样的包装永远不应该很复杂,因为重点是避免膨胀!
如果元膨胀是另一个问题(我刚刚读到你正在使用代码生成器,并且担心成千上万这样的模板包装器的源代码大小),你可能会考虑稍微改变一下接口:
template< typename T, char (*func)( std::string const & ) >
T get_anything( std::string const &s ) {
return T( func( s ) );
}
这样,可以有许多get_something()
函数,并且它们都可以用作get_anything
的第二个模板参数。您也可以使用指向成员的指针而不是函数指针作为模板参数。
- 从C++实例化QML
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何创建一个空的全局类并在启动时实例化它
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 约束和显式模板实例化
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 对象实例化调用构造函数的次数太多
- 如何使用非默认构造函数实例化模板化类
- 静态数据成员模板专用化的实例化点在哪里
- 错误的cv::face FacemarkLBF实例化
- C++的解析器在可以区分比较和模板实例化之前会做什么?
- 为什么 gcc 和 clang 为函数模板的实例化生成不同的符号名称?
- 检查某些类型是否是模板类 std::optional 的实例化
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 无法使用 SWIG 在 Python 中实例化C++类(获取属性错误)
- 模板化类构造函数的模板实例化
- 在 c++ 中的模板实例化中使用带有构造函数的类作为类型参数
- 受约束的成员函数和显式模板实例化
- 对显式实例化的模板函数的未定义引用
- g++模板实例化器有多聪明(避免模板膨胀)