C++模板元编程详细教程(之三)

本文深入探讨C++模板编程中的模板特化概念,包括全特化与偏特化的使用场景及链接方式,并通过示例代码讲解如何实现特定类型的定制化处理。

先序文章请看
C++模板元编程详细教程(之一)
C++模板元编程详细教程(之二)

模板特化

有了前两篇的基础,相信大家对模板编程已经有一点初步的感觉了。趁热打铁,这一篇我们主要来介绍一下模板特化。

首先来看一下下面的例子:

template <typename T>
void add(T &t1, const T &t2) {
   
   
  t1 += t2;
}

上面是一个简单的模板函数,用于把第二个参数的值加到第一个参数中去。这个模板函数对于基本数据类型的实例化都是没什么问题的,但是如果是字符串的话,那将会有问题:

void Demo() {
   
   
  int a = 1, b = 3;
  add(a, b); // add<int>,调用符合预期
  
  char c1[16] = "abc";
  char c2[] = "123";

  add(c1, c2); // add<char *>, 调用不符合预期
}

这里的问题就在于,对于字符串类型(这里指原始C字符串,而不是std::string)来说,「相加」并不是简单的+=,因为字符串主要是用字符指针来承载的,指针相加是不合预期的。我们希望的是字符串拼接。

因此,我们希望,单独针对于char *的实例化能够拥有不同的行为,而不遵从「通用模板」中的定义。这种语法支持就叫做「特化」,或「特例」。可以理解为,针对于模板参数是某种特殊情况下进行的特殊实现。

因此,我们在通用模板的定义基础上,再针对char *类型定义一个特化:

#include <cstring>

template <typename T>
void add(T &t1, const T &t2) {
   
   
  t1 += t2;
}

template <> // 模板特化也要用模板前缀,但由于已经特化了,所以参数为空
void add<char *>(char *&t1, char *const &t2) {
   
    // 特化要指定模板参数,模板体中也要使用具体的类型
  std::strcat(t1, t2);
}

void Demo() {
   
   
  int a = 1, b = 3;
  add(a, b); // add<int>是通过通用模板生成的,因此本质是a += b,符合预期

  char c1[16] = "abc";
  char c2[] = "123";

  add(c1, c2); // add<char *>有定义特化,所以直接调用特化函数,因此本质是strcat(c1, c2),符合预期
}

上例简单展示了一下模板特化目标解决的问题,和其基本的语法。但其实模板特化远不止如此,它有着巨大的潜力。

模板的特化分两种情况,一种是全特化(有的地方也叫特例),一种是偏特化(有的地方也叫部分特化)。全特化相对简单一些,笔者会先来介绍。而偏特化会伴随SFINAE理论,它将会成为模板元编程最核心的理论基础。

全特化与模板的链接方式

首先复习一下我们在开篇时候所提到的一个非常重要的概念。**模板本身不是可使用的代码,而是一种代码的升成方法。需要经过实例化后才能成为实际可用的代码。**比如说模板函数需要指定模板参数(可以是显式指定,也可以是编译器自动推导)实例化后,才能成为函数,同理,模板类也需要实例化后才能成为类。

然而「全特化」就是说,当所有模板参数都指定了的时候,才叫「全」。那么上一节中add的示例就是一个全特化,因为它原本只有一个模板参数,把它特化了自然是「完全」特化的。

而要谈到全特化,就不得不谈到一个非常容易踩坑的点,那就是模板的链接方式。在一个单独的.cpp文件中使用模板并不会有什么链接性问题,但如果在多个文件中都要使用呢?自然要通过「头文件声明+链接」的方式来完成了。

但模板本身又很特殊,它本身不是可用的代码,而是代码生成器,因此编译器会在编译期用模板来生成代码,注意,这个时候还没有开始链接!所以问题就产生了,假如我们按照直觉和习惯,把模板的声明和定义分文件来写,会怎么样呢?请看下面示例:

tmp.h

#pragma once

template <typename T>
void f(const T &t); // 声明

tmp.cpp

#include "tmp.h"

template <typename T>
void f(const T &t) {
   
   
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

borehole打洞哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值