2

我一直在阅读一些关于templates 的页面。

我看到了,templates 是这样使用的:

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);

在这段代码中,模板被赋予了真实和绝对类型,恕我直言,我们可以在过滤器的参数列表中添加另外四个参数,而不是使用模板。

那么,为什么要这样使用模板呢?

4

1 回答 1

3

templates 不是函数。 templates 是函数的工厂,其中的参数决定了生成哪个函数。

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函数,而是定义了一整套这样的函数。

<>after中的filter参数是传递给template函数工厂以确定实际生成的函数的参数。

当这些函数由工厂生成时(在编译时),传入template函数工厂的值是已知的。所以围绕这些常数进行优化非常容易。

确实,内联和 as-if 规则可以允许编译器接受文字参数,推断给定参数是编译时常量,并在包含该常量的情况下编译函数——但这种技术既脆弱又有限。

对于template工厂生产的功能,它是一个实际功能。因此,您可以存储指向它的指针并将其作为无状态函数传递。

举一个具体的例子,假设您要处理图像。现在处理图像的实用方法是使用一些数据建立一个基于扫描线的函数,然后迭代图像的扫描线,将每个扫描线传递给基于扫描线的函数。

另一方面,许多图像变换是逐像素变换。在每个图像转换函数中对这些像素编写所有优化循环会导致大量复制粘贴代码。但是您不能将指向基于像素的操作的指针传递给扫描线处理函数——与典型的每像素操作相比,取消引用指针的开销有点高。

因此,您创建了一个template函数工厂,它在编译时采用每像素函数,并将其包装在对像素代码的迭代中。开销就消失了。

template更重要的是,您可以在另一个函数工厂(通过 a )中传递像素操作,而不是像素操作template class,因此您可以执行诸如设置(如果您正在执行SSE类型组装)、每像素操作一次应处理多少像素等操作。

结果可能是一堆用ifs 编写的代码,看似充满了分支和条件,但当您将所有参数作为模板参数传入时,实际上编译为几乎完全平坦且无分支的扫描线操作。

简而言之,templates 有助于解决传统上通过代码生成解决的问题——无论是宏还是 3rd 方工具。它们足够强大和方便,人们可以在您以前从未烦恼过的情况下使用它们来生成代码,例如为每种用户类型生成自定义容器、自定义搜索算法、散列算法、迭代、for 循环以及无数其他愚蠢的。

templates 可以做的 所有事情都可以在没有它们的情况下完成,但这并不奇怪:当心图灵 tar 坑,那里一切都是等价的,但没有什么是容易的。templates 使某种代码生成变得容易

于 2013-04-28T02:47:57.190 回答