移动学习网 导航

C++ 模板元编程系列专题 2:模板特化的概念从何而来

2024-05-01m.verywind.com
~

如果元编程中所有的变量(或者说元编程的参数),都是类型,那么这样的编程,我们有个特定的称呼,叫“泛型”。

模板的发明,仅仅是为了做和 宏 几乎一样的替换工作吗?可以说是,也可以说不是。

一方面, 模板 可以用来替换类型,这点和宏没什么区别。只是宏在编译阶段基于文本做纯粹替换,被替换的文本本身没有任何语义。而模板会在分析模板时以及实例化模板的时候都会进行检查,而且源代码中也能与调试符号一一对应,所以无论是编译时还是运行时,排错都相对简单。

另一方面,模板和宏也有很大的不同,模板最大的不同在于它是 “可以运算” 的。我们来看一个例子:

上述函数实现了一个 uint8_t 和 uint8_t 类型的加法运算,如果现在要实现 int16 和 int16 类型的加法运算,该怎么办呢?简单点的方法如下:

但是这里有两个难点:

有人说, 重载 虚函数 也能解决如上问题:

甚至在 C 语言中定义新的 结构体 Variant 或使用 void* 也能解决该问题:

没错,但是如果我还有 uint9_t、uint10_t 等各种类型的加法运算呢?Anyway,不 管是哪种方法都很难避免 if/else 的存在。

模板与上述这些方法最大的区别在于:模板无论其参数或者是类型,它都是一个编译期分派的方法。编译期就能确定的东西既可以做类型检查,编译器也能进行优化,砍掉任何不必要的代码执行路径。

我们先来看看一个模板的例子:

那么当传入两个不同类型的变量,或者不是 in t 和 float 变量,编译器就会提示错误。

从能力上来看,模板能做的事情都是编译期完成的。编译期完成的意思就是,当你编译一个程序的时候,所有的量就都已经确定了。比如下面的例子:

从 上述代码中我们可以看到:aVar 和 bVar 都一定会是整数。所以 如果有合适的机制,编译器就能知道此处的 AddFloatOrMulInt 中只需要执行 int 路径上的代码,而且编译器在此处也能单独为 int 路径生成代码,从而去掉那个不必要的 if 。在模板代码中,这个“合适的机制”就是指“特化”和“部分特化(Partial Specialization)”,后者也叫“偏特化”。

1.0 版本 - 伪代码

2.0 版本 - 函数重载

3.0 版本 - 纯模板

好吧,问题来了!如何要让两个内容不同,但是模板参数形式相同的类进行区分呢?特化!特化(specialization)是根据一个或多个特殊的整数或类型,给出模板实例化时的一个指定内容

4.0 版本 - 模板特化

解释:

至此,第一个模板特化的代码已经写完了。这里的 AddFloatOrMulInt 如同是一个函数,却只能在编译期间执行。如果你体味到了这一点,那么恭喜你,你的模板元编程已经开悟了。

本文核心只讲了两个问题:一是为什么需要泛型编程,重点介绍了宏、模板和元编程的关系;二是模板类的特化代码如何编写。关于特化,还有很多细节知识,在之后的文章中我们继续探究,另外将还介绍偏特化等知识点,敬请期待。



户户网菜鸟学习
联系邮箱
返回顶部
移动学习网