模板与常规函数的优化:幕后发生了什么?
Optimization of template vs regular function: what's going on under the hood?
从的回答中,我发现了一个比较有趣的现象。给出以下两个函数:
void require(void * volatile) { }
template <typename T>
void requireT(T * volatile) { }
用指向静态数据成员的指针调用 each 将强制实例化该成员(这是另一个问题的目的),但是,requireT
将被完全优化,而 require
对结果 code/binary (g++ 4.9.2).
有影响
这是为什么?编译器处理代码的方式有何不同?
在需要的地方生成模板代码,因为事先为可能使用的每种可能类型生成它是不可行的。当对模板函数的调用被优化时,编译器没有理由自行生成函数。
几乎总是会生成普通函数,即使对它们的所有调用都被优化掉了,因为它们也可能从不同的编译单元调用。通过将 static 放在编译单元之前或将其放在未命名的命名空间中使函数成为编译单元的本地函数可能有助于编译器完全优化函数。
模板是隐式的inline
,但其他函数不是。
链接器可能假定未使用的 inline
函数不需要包含在已编译的二进制文件中,因为任何客户端代码总能在头文件中找到它。 (不过,在生成可执行应用程序二进制文件时,通常会删除所有未使用的函数。)
从
void require(void * volatile) { }
template <typename T>
void requireT(T * volatile) { }
用指向静态数据成员的指针调用 each 将强制实例化该成员(这是另一个问题的目的),但是,requireT
将被完全优化,而 require
对结果 code/binary (g++ 4.9.2).
这是为什么?编译器处理代码的方式有何不同?
在需要的地方生成模板代码,因为事先为可能使用的每种可能类型生成它是不可行的。当对模板函数的调用被优化时,编译器没有理由自行生成函数。
几乎总是会生成普通函数,即使对它们的所有调用都被优化掉了,因为它们也可能从不同的编译单元调用。通过将 static 放在编译单元之前或将其放在未命名的命名空间中使函数成为编译单元的本地函数可能有助于编译器完全优化函数。
模板是隐式的inline
,但其他函数不是。
链接器可能假定未使用的 inline
函数不需要包含在已编译的二进制文件中,因为任何客户端代码总能在头文件中找到它。 (不过,在生成可执行应用程序二进制文件时,通常会删除所有未使用的函数。)