2.1节写作结束。

This commit is contained in:
Ye WU 2014-03-01 00:06:21 -08:00
parent c3a9257a8a
commit a86fa7ee32

View File

@ -579,6 +579,87 @@ public:
这个技术的名字,并不叫“模板”,而是叫“元编程”。 这个技术的名字,并不叫“模板”,而是叫“元编程”。
meta无论在中文还是英文里都是个很“抽象abstract”的词。因为它的本意就是“抽象”。元编程也可以说就是“编程的抽象”。用更好理解的说法元编程意味着你撰写一段程序A程序A会运行后生成另外一个程序B程序B才是真正实现功能的程序。那么这个时候程序A可以称作程序B的元程序撰写程序A的过程就称之为“元编程”。
回到我们的堆栈的例子。真正执行功能的其实仍然是浮点的堆栈、整数的堆栈、各种你所需要的类型的堆栈。但是因为这些堆栈之间太相似了仅仅有着些微的不同我们为什么不能有一个将相似之处囊括起来同时又能分别体现出不同之处的程序呢很多语言都提供了这样的机会。C中的宏C++中的模板Python中的Duck Typing广义上将都能够实现我们的思路。
我们的目的是找出程序之间的相似性进行“元编程”。而在C++中,元编程的手段,可以是宏,也可以是模板。
宏的例子姑且不论,我们来看一看模板:
``` C++
template <typename T>
class Stack
{
public:
void push(T v);
T pop();
Int Find(T x)
{
for(Int i = 0; i <= size; ++i)
{
if(data[i] == x) { return i; }
}
}
// ... 其他代码 ...
};
typedef Stack<int> StackInt;
typedef Stack<float> StackFloat;
```
通过模板,我们可以将形形色色的堆栈代码分为两个部分,一个部分是不变的接口,以及近乎相同的实现;另外一部分是元素的类型,它们是需要变化的。因此同函数类似,需要变化的部分,由模板参数来反应;不变的部分,则是模板内的代码。可以看到,使用模板的代码,要比不使用模板的代码简洁许多。
如果元编程中所有的变化的量(或者说元编程的参数),都是类型,那么这样的编程,我们有个特定的称呼,叫“泛型”。
但是你会问,模板的发明,仅仅是为了做和宏几乎一样的替换工作吗?可以说是,也可以说不是。一方面,很多时候模板就是为了替换类型,这个时候作用上其实和宏没什么区别。只是宏是基于文本的替换,被替换的文本本身没有任何语义。只有替换完成,编译器才能进行接下来的处理。而模板会在分析模板时以及实例化模板时时候都会进行检查,而且源代码中也能与调试符号一一对应,所以无论是编译时还是运行时,排错都相对简单。
但是模板也和宏有很大的不同否则此文也就不能成立了。模板最大的不同在于它是“可以运算”的。我们来举一个例子不过可能有点牵强。考虑我们要写一个向量逐分量乘法。只不过这个向量它非常的大。所以为了保证速度我们需要使用SIMD指令进行加速。假设我们有以下指令可以使用
```
Int8,16: N/A
Int32 : VInt32Mul(int32 * 4, int32 * 4)
Int64 : VInt64Mul(int64 * 2, int64 * 2)
Float : VInt64Mul(float * 2, float * 2)
```
所以对于Int8和Int16我们需要提升到Int32而Int32和Int64各自使用自己的指令。所以我们需要实现下的逻辑
``` C++
for(v4a, v4b : vectorsA, vectorsB)
{
if type is Int8, Int16
VInt32Mul( ConvertToInt32(v4a), ConvertToInt32(v4b) )
elif type is Int32
VInt32Mul( v4a, v4b )
elif type is Float
...
}
```
这里的问题就在于,如何根据 `type` 分别提供我们需要的实现这里有两个难点。首先if type == xxx 是不存在于C++中的。第二,即便存在根据 `type` 的分配方法我们也不希望它在运行时branch这样会变得很慢。我们希望它能按照类型直接就把代码编译好就跟直接写的一样。
嗯,聪明你果然想到了,重载也可以解决这个问题。
``` C++
GenericMul(int8 * 4, int8 * 4);
GenericMul(int16 * 4, int16 * 4);
GenericMul(int32 * 4, int32 * 4);
GenericMul(int64 * 4, int64 * 4);
// 其它 Generic Mul ...
for(v4a, v4b : vectorsA, vectorsB)
{
GenericMul(v4a, v4b);
}
```
这样不就可以了吗?
唔,你赢了,是这样没错。但是问题是,我这个平台是你可没见过,它叫 `Deep Thought` 特别缺心眼儿,不光有 `int8`,还有更奇怪的 `int9`, `int11`,以及可以代表世间万物的 `int42`。你总不能为之提供所有的重载吧?这简直就像你枚举了所有程序的输入,并为之提供了对应的输出一样。
好吧,我承认这个例子还是太牵强了。不过相信我,在你阅读完第二章和第三章之后,你会将这些特性自如地运用到你的程序之中。你的程序将会变成体现模板“可运算”威力的最好例子。
###2.2 模板世界的If-Then-Else类模板的特化与偏特化 ###2.2 模板世界的If-Then-Else类模板的特化与偏特化
###2.3 函数模板的重载、参数匹配、特化与部分特化 ###2.3 函数模板的重载、参数匹配、特化与部分特化
###2.4 技巧单元:模板与继承 ###2.4 技巧单元:模板与继承