我一直在阅读有关template
s。
我看到,template
s的用法如下:
template <typename T>
T func(T a) {...}
因此,它可以灵活地为不同类型的变量使用相同的代码。 而且,我们可以使用似乎只适用于类的专业化,它就像:
template <> class A<int> {....}
但我没有找到这样的用法:
template<int N, bool isVertical, bool isFirst, bool isLast>
static void filter(int bitDepth, Pel const *src, int srcStride,
short *dst, int dstStride, int width,
int height, short const *coeff);
它被称为:
filter<N, false, true, true>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);
在这段代码中,模板给出了真实和绝对类型,恕我直言,我们可以在过滤器的参数列表中添加另外四个参数,而不是使用模板。
那么,为什么模板会像这样使用?
答案 0 :(得分:3)
template
不是函数。 template
是函数的工厂,其中参数确定生成哪个函数。
(template
函数也有基于参数的类型推导,但这只是确定template
函数工厂生成的函数。)
所以这个:
template<int N, bool isVertical, bool isFirst, bool isLast>
static void filter(int bitDepth, Pel const *src, int srcStride,
short *dst, int dstStride, int width,
int height, short const *coeff);
没有定义一个filter
函数,而是一整套这样的函数。
<>
之后的filter
中的参数是传递给template
函数工厂的参数,用于确定实际生成的函数。
当这些函数由工厂生成时(在编译时),传递到template
函数工厂的值是已知的。因此围绕这些常数进行优化非常简单。
内联和as-if规则确实可以允许编译器接受文字参数,推断给定参数是编译时常量,并编译包含该常量的函数 - 但这种技术既脆弱又有限的。
使用template
工厂生成的函数,它是一个实际的函数。因此,您可以存储指向它的指针并将其作为无状态函数传递。
有关具体示例,假设您要处理图像。现在处理图像的实际方法是使用一些数据设置基于扫描线的函数,然后迭代图像的扫描线,将每个扫描线传递给基于扫描线的函数。
另一方面,许多图像变换是逐像素变换。在每个图像变换函数中对这些像素写入所有优化的循环会产生大量的复制粘贴代码。但是你不能将指向基于像素的操作的指针传递给扫描线处理函数 - 与典型的每像素操作相比,取消引用指针的开销要高一些。
因此,您创建一个template
函数工厂,它在编译时采用每像素函数,并在像素代码的迭代中包装它。开销就消失了。
更多的是,您可以在另一个template
函数工厂(通过template class
)传递像素操作,因此如果您正在进行SSE
类型,则可以执行设置操作汇编,每像素操作一次应处理的像素数等等。
结果可能是一堆用if
编写的代码,似乎充满了分支和条件,但实际上在传入所有参数时会编译成几乎完全平坦且无分支的扫描线操作作为模板参数。
简而言之,template
有助于解决传统上通过代码生成解决的问题 - 无论是宏还是第三方工具。它们足够强大和方便,人们在以前从未打扰过的情况下使用它们来生成代码,例如为每种用户类型生成自定义容器,自定义搜索算法,散列算法,迭代,for循环以及无数其他胡闹。
template
可以做的所有事情都可以在没有它们的情况下完成,但这并不奇怪:要注意图灵焦油坑,其中一切都是等价的,但没有任何重要的东西。 template
生成某种代码 easy 。