更新: 我在Reddit上被告知这是C++编程和编译器优化的一个旧观点。如果你熟悉模板元编程并想了解最新情况,请前往Reddit的评论帖子。
一个模板及其阶乘的用法。
什么是模板元编程?
“元编程指的是程序具有自我知识或可以操作自身的各种方式。”维基百科如此写道。当我作为一名新手研究生第一次读到这句话时,我的想象被AI程序能够编写其他代码片段并接管世界的可能性所震撼。虽然这是好莱坞版本,但这是一种在工业界使用C++模板进行编译时优化的现实。
我在研究一项涉及优化的项目时遇到了这个概念。虽然它不是远离CS101课程基础的概念,但它并不是很广为人知,因此写了这篇博客来介绍它。
让我们首先看看什么是模板:模板是泛型编程的基础,它涉及以与任何特定变量类型无关的方式编写代码。
例如,看一下以下代码:
#include <iostream>using namespace std;
// One function works for all data types. template <typename T>
T tMax(T x, T y)
{ return (x > y)? x: y; }
int main()
{cout << tMax<int>(3, 7) << endl; // Call myMax for type int
cout << tMax<double>(3.0, 7.0) << endl; // call myMax for type d
cout << tMax<char>(’g’, 'e’) << endl; // call myMax for type charreturn 0;
}
模板tMax
提供了一个通用函数,可被所有数据类型使用。
模板如何工作?
模板通过汇编器编译为实际类型
模板本身在程序编译后不存在,即在汇编代码中不存在。
如上面我们看到的tMax
模板示例中,模板tMax
会在编译时转换为针对代码中调用的每种类型的不同函数。参考:汇编代码(第38行定义了函数_int tMax<int>(int,int)_,它是int类型的tMax)
你可能希望转到Tutorials Point(C++ Templates)花费一些时间学习模板。Java提供的等效构造是Generics。
使用模板进行优化
由于模板在编译时“展开”为函数,并且编译器在编译时执行变量不涉及的计算,因此一些计算可以在编译时完成并在运行时避免。
计算可以在编译时完成并在运行时避免
假设你正在编写一段代码,并且你想要在代码中使用值为15!(15的阶乘)。递归的方法是使用以下代码:
long factorial(int n)
{
if (n == 0)
return 1;
else
return(n * factorial(n-1));
}int main()
{
cout << factorial(15) << endl;
return 0;
}
这将编译为一个函数和对该函数的调用,其中参数n=15将在运行时执行。参考:汇编代码(你可以在第17行看到阶乘函数和一个_call factorial(int)_)
现在让我们将其转换为使用模板的程序。
template <long N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};template <>
struct Factorial<0>
{
enum { value = 1 };
};int main()
{
cout << Factorial<15>::value << endl;
return 0;
}
在编译时,编译器将模板Factorial
展开为参数N=15,并遇到参数N=14的模板,依此类推,直到N=0。由于这些计算中没有遇到任何变量,因此它们在编译时解决,Factorial<15>最终在编译时解决为15!即1307674368000,从而避免了任何运行时计算。你可以在组装代码中看到直接使用值1307674368000而不是调用函数。参考:汇编代码(没有函数定义,因为汇编器已经计算出了15!的值,并在第4行中使用)
使用时间分析(和太多迭代)执行两个实现可以显示优化的影响。模板化代码运行时间约为0.03ms,而递归版本运行时间长至少30倍,如预期的那样,在运行时计算阶乘并进入递归。
其他示例:模板和专用函数。
更深入(更复杂,更有价值)的示例来自Template Metaprograms(Todd Veldhuizen)。使用模板进行冒泡排序似乎不太直观,因为输入不是单个数字,但是想法是创建一个模板,该模板展开为适用于特定元素数量的专用函数
进行冒泡排序。我强烈建议阅读该示例并解决详细问题。
这在哪里使用?
我首先想到的是为什么我们需要执行像调用模板的函数这样的函数,如果没有涉及变量。这不等同于使用值本身编写代码(例如,在Factorial<15>
的位置上放置1307674368000)。对此的反驳是需要抽象函数,而不是在代码中随意散布值。
因此,Boost库利用模板提供了复杂函数的库,例如阶乘,正弦,余弦等,以确保在可以在编译时完成计算的任何地方不在运行时计算值。总之,模板是一种很好的技巧,可以确保编译时计算,并在时间关键项目中节省一些运行时间(高频交易,可能是航空和航天)。
请查看SO以获取有关模板元编程的社区讨论。如果你在你的代码中使用过,请在下面发表评论并分享你的经验。
译自:https://levelup.gitconnected.com/template-metaprogramming-a-c-walkthrough-a7c6db0b4148
评论(0)