一个常见的例子是排序。
In C, qsort
接受一个指向比较函数的指针。一般来说,都会有一份qsort
代码,未内联。它将通过指向比较例程的指针进行调用——当然这也不是内联的。
In C++, std::sort
是一个模板,它可以将函子对象作为比较器。有一个不同的副本std::sort
对于用作比较器的每种不同类型。假设您使用重载的函子类operator()
,然后调用比较器can很容易被内联到这个副本中std::sort
.
因此,模板为您提供了更多内联,因为有更多的副本sort
代码,每个代码都可以内联不同的比较器。内联是一个相当好的优化,并且排序例程会进行大量比较,因此您可以经常测量std::sort
比同类跑得更快qsort
。这样做的代价是可能会出现更大的代码——如果您的程序使用许多不同的比较器,那么您将获得排序例程的许多不同副本,每个副本都包含不同的比较器。
原则上,C 实现没有理由不能内联qsort
进入它被称为的地方。然后,如果使用函数名称调用它,理论上优化器可以观察到在使用它时,函数指针must仍然指向相同的功能。然后它可以内联对该函数的调用,结果将类似于std::sort
。但在实践中,编译器往往不会迈出第一步,内联qsort
。这是因为(a)它很大,并且(b)它位于不同的翻译单元中,通常编译到您的程序链接的某个库中,并且(c)要这样做,您将有一个内联副本qsort
对于每次调用它,而不仅仅是每个不同比较器的副本。因此它会比 C++ 更加臃肿,除非实现也能找到一种方法来在以下情况下通用代码:qsort
在不同的地方用相同的比较器调用。
因此,通用函数如qsort
由于通过函数指针或其他间接调用[*],C 中往往会产生一些开销。 C++ 中的模板是保持源代码通用但确保其编译为专用函数(或多个此类函数)的常用方法。专用代码有望更快。
值得注意的是,模板绝不只是与性能有关。std::sort
本身比qsort
在某些方面。例如qsort
只对数组进行排序,而std::sort
可以对提供随机访问迭代器的任何内容进行排序。例如,它可以对deque
,其内部是几个单独分配的不相交数组。因此,使用模板并不一定会带来任何性能优势,可能是出于其他原因。碰巧模板确实会影响性能。
[*] 另一个排序示例 -qsort
接受一个整数参数,表示数组的每个元素有多大,因此当它移动元素时,必须调用memcpy
或与此变量的值类似。std::sort
在编译时知道元素的确切类型,从而知道确切的大小。它可以内联一个复制构造函数调用,该调用反过来可能会转换为复制该字节数的指令。与内联比较器一样,通常可以比调用复制可变字节数的例程(向其传递值 4(或 8)来更快地复制 4(或 8、或 16 或其他)字节)。 ,或 16,或其他)。和以前一样,如果你打电话qsort
具有大小的字面值,并且调用qsort
被内联,那么编译器可以在 C 中执行完全相同的优化。但实际上您看不到这一点。