模板元编程是否比同等的C代码更快?

模板元编程是否比同等的C代码更快? (我说的是运行时性能):)

首先,免责声明:我认为你所询问的不仅仅是模板元编程,还包括通用编程。 这两个概念密切相关,并没有确切定义每个概念。 但简而言之,模板元编程实质上是使用模板编写程序,模板在编译时进行评估。 (它使它在运行时完全免费。没有任何反应。值(或更常见的类型)已由编译器计算,并可作为常量(const变量或枚举)或typedef使用嵌套在一个类中(如果你用它来“计算”一个类型)。

通用编程使用模板,并在必要时使用模板元编程,创建通用代码,它们具有相同的function(并且没有性能损失),具有全部和任何类型。 我将在下面使用两者的示例。

模板元编程的一个常见用途是使类型能够用于generics编程,即使它们不是为它设计的。

由于模板元编程技术上完全是在编译时进行的,因此您的问题与通用编程有点相关,通用编程仍然在运行时进行,但是效率很高,因为它可以专门用于编译时使用的精确类型。

无论如何…


取决于您如何定义“等效C代码”。

关于模板元编程(或一般的通用编程)的技巧是它允许将大量计算移动到编译时,并且它使得灵活的参数化代码能够像硬编码值一样高效。

这里显示的代码例如在编译时计算斐波纳契序列中的数字。

C ++代码’ unsigned long fib11 = fibonacci<11uL>::value ‘依赖于该链接中定义的模板元程序,并且与C代码’ unsigned long fib11 = 89uL ‘一样高效。 模板在编译时进行评估,产生一个可以分配给变量的常量。 因此,在运行时,代码实际上与简单赋值相同。

因此,如果这是“等效C代码”,性能是相同的。 如果等效的C代码是“一个可以计算任意斐波纳契数的程序,用于查找序列中的第11个数字”,则C版本会慢得多,因为它必须作为函数实现,它计算值在运行时。 但这是“等效C代码”,因为它是一个C程序,具有相同的灵活性(它不仅仅是一个硬编码常量,而是一个可以返回fibonacci序列中任何数字的实际函数)。

当然,这通常不常用。 但它几乎是模板元编程的典型例子。

通用编程的一个更现实的例子是排序。

在C中,您有qsort标准库函数,它带有一个数组和一个比较器函数指针。 无法内联对此函数指针的调用(除非在普通情况下),因为在编译时,不知道将调用哪个函数。

当然,替代方案是为您的特定数据类型设计的手写排序function。

在C ++中,等效的是函数模板std::sort 。 它也需要一个比较器,但它不是一个函数指针,而是一个函数对象,如下所示:

 struct MyComp { bool operator()(const MyType& lhs, const MyType& rhs) { // return true if lhs < rhs, however this operation is defined for MyType objects } }; 

这可以内联。 std::sort函数传递一个模板参数,因此它知道比较器的确切类型,因此它知道比较器函数不仅仅是一个未知的函数指针,而是MyComp::operator()

最终结果是C ++函数std::sort与同一排序算法的C中的手动编码实现完全一样高效。

所以再次,如果那是“等效的C代码”,那么性能是相同的。 但是,如果“等效C代码”是“可以应用于任何类型的通用排序函数,并允许用户定义的比较器”,那么C ++中的通用编程版本效率要高得多。

这真的是诀窍。 通用编程和模板元编程不是“比C更快”。 它们是实现通用,可重用代码的方法其速度与手动编码和硬编码C一样快

这是一种充分利用这两个世界的方法。 硬编码算法的性能,以及一般参数化算法的灵活性和可重用性。

模板元编程(TMP)在编译时是“运行”的,所以在将它与普通的C / C ++代码进行比较时,并不是真的比较苹果和苹果。

但是,如果你有TMP评估的东西,那么根本没有运行时成本。

如果你的意思是可重复使用的代码 ,那么毫无疑问是肯定的。 元编程是生成库的一种优越方式,而不是客户端代码。 客户端代码不是通用的,它是为了做特定的事情而编写的。

例如,查看来自C标准库的qsort和C ++标准排序 。 这就是qsort的工作原理:

 int compare(const void* a, const void* b) { return (*(int*)a > *(int*)b); } int main() { int data[5] = {5, 4, 3, 2, 1}; qsort(data, 5, sizeof(int), compare); } 

现在看看排序

 struct compare { bool operator()(int a, int b) { return a < b; } }; int main() { int data[5] = {5, 4, 3, 2, 1}; std::sort(data, data+5, compare()); } 

sort更清晰,更安全,更高效,因为比较函数在排序中内联。 在我看来,这是元编程的好处, 你编写通用代码 ,但编译器生成的代码就像手工编写的代码一样


我发现元编程非常漂亮的另一个地方是当你编写像boost :: spirit或boost :: xpressive这样的库时,你可以在C ++中编写EBNF并让编译为你检查EBNF语法,用xpressive你可以编写正则表达式并让编译器也检查你的正则表达式语法!


我不确定提问者是否通过TMP在编译时计算值。 这是我用boost编写的一个例子:)

 unsigned long greatestCommonDivisor = boost::math::static_gcd<25657, 54887524>::value; 

你用C做什么你不能模仿上面的代码,基本上你必须手工计算它然后将结果分配给greatCommonDivisor

答案取决于它。

模板元编程可以用来轻松编写递归下降语言解析器,与精心设计的C程序或基于表的实现(例如flex / bison / yacc)相比,这些解析器效率低下。

另一方面,您可以编写生成展开循环的元程序,这比使用循环的传统C实现更有效。

主要好处是元程序允许程序员用更少的代码完成更多工作。

不利的一面是,它还会给你一把枪,用脚射击自己。

模板元编程可以被认为是编译时执行。

编译时编译代码需要更长的时间,因为它必须编译然后执行模板,生成代码,然后再次编译。

我不确定的运行时开销,它不应该比你自己用C代码自己编写的那样多。

我参与了另一个程序员尝试过元编程的项目。 太可怕了。 这是一个彻头彻尾的头痛。 我是一名具有大量C ++经验的普通程序员,并试图设计他们想要做的事情比他们直接写出来的时间要多得多。

由于这种经验,我厌倦了C ++ MetaProgramming。

我坚信最好的代码最容易被普通开发人员阅读。 这是第一优先级的软件的可读性。 我可以使用任何语言进行任何工作……但技能是让它对于项目中的下一个人来说可读并且易于使用。 C ++ MetaProgramming无法通过集合。

模板元编程在性能方面不会给你任何神奇的力量。 它基本上是一个非常复杂的预处理器; 你总是可以在C或C ++中编写等价的东西,它可能会花费你很长时间。

我不认为有任何炒作,但C ++ FAQ给出了一个关于模板的清晰简单的答案: https : //isocpp.org/wiki/faq/templates#overview-templates

关于原始问题:它无法回答,因为那些东西不具有可比性。