C++ 深入浅出探索模板

目录
  • 非类型模板参数
  • 模板特化
    • 函数模板特化
    • 类模板特化
      • 全特化
      • 偏特化
    • 模板分离编译
      • 模板的分离编译
      • 解决方法
  • 总结

非类型模板参数

模板参数分类类型形参与非类型形参。

类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

注意:

  • 浮点数,类对象以及字符串是不允许作为非类型模板的。
  • 非类型的模板参数必须在编译期就能确认结果。

模板特化

有时候,编译默认函数模板或者类模板不能正确处理需要逻辑,需要针对一些情况特殊化处理,就要做模板特化。

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,例如:

template<class T>
bool IsEqual(const T&left,const T&right)
{
    return left==right;
}
//针对字符串类型要特殊化处理--写一份函数模板的特殊化出来
template<>
bool IsEqual<char*>(const char* &left,const char* &right)
{
    return strcmp(left,right)==0;
}
​
int main()
{
    cout<<IsEqual(1,2)<<endl;//0
    cout<<IsEqual(1.1,1.1)<<endl;//1

    char p1[]="hello";
    char p2[]="hello";
    cout<<IsEqual(p1,p2)<<endl;//0

    const char* p3="hello";
    const char* p4="hello";
    cout<<IsEqual(p3,p4)<<endl;//1
}

此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

特化的本质:显示指定实例化的模板

函数模板特化

函数模板特化步骤

  • 必须要先有一个基础的函数模板
  • 关键字template后面接一对空的尖括号<>
  • 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  • 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
template<>
bool IsEqual<const char* const>(const char* const &left,const char* &right)
{
    if(strcmp(left,right)>0)
        return true;
    return false;
}

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。

类模板特化

全特化

全特化既是将模板参数列表中所有的参数都确定化

template<calss T1,class T2>
class Data
{
public:
    Data(){cout<<"Data<T1,T2>"<<endl; }
private:
          T1 _d1;
          T2 _d2;
};
​
template<>
class Data<int ,char>
public:
    Data(){cout<<"Data<int,char>"<<endl; }
private:
          int _d1;
          char _d2;
};
void TestVector()
{
    Data<int,int>d1;
    Data<int,char>d2;
}

偏特化

针对任何模板参数进一步进行条件限制设计的特化版本,比如对于一下模板类:

template<class T1,class T2>
class Data
{
public:
    Data(){cout<<"Data<T1,T2>"<<endl;}
private:
    T1 _d1;
    T2 _d2;
};
//偏特化有以下两种表现方式:
//1、部分特化:将函数中的一部分参数特化。
​
template<class T1>
class Data<T1,int>
{
public:
    Data(){cout<<"Data<T1,int>"<<endl;}
private:
    T1 _d1;
    int _d2;
}
//2、参数进一步的限制:偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
//两个参数偏特化为指针类型
template<typename T1,typename T2>
class Data<T1*,T2*>
{
public:
    Data(){cout<<"Data<T1*,T2*>"<<endl;}
private:
    T1* _d1;
    T2* _d2;
};
//两个参数偏特化为引用类型
template<typename T1,typename T2>
class Data<T1&,T2&>
{
public:
    Data(){cout<<"Data<T1&,T2&>"<<endl;}
private:
    const T1& _d1;
    const T2& _d2;
};
​
void test2()
{
    Data<double,int>d1;   //调用特化的int版本
    Data<int,double>d2;   //调用基础模板
    Data<int *,int*>d3;   //调用特化的指针版本
    Data<int&,int&>d4(1,2); //调用特化的引用版本
}

模板分离编译

分离编译就是指一个程序由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将目标文件连接起来形成单一的可执行文件的过程称为分离编译模式。

模板的分离编译

// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
    return left + right;
}
// main.cpp
#include"a.h"
int main()
{
    Add(1, 2);
    Add(1.0, 2.0);
    return 0;
}

分析:

解决方法

  • 将声明和定义放到一个文件中。推荐使用
  • 模板定义的位置显示实例化。不实用

总结

优点

  • 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而生
  • 增强了代码的灵活性

缺点

  • 模板会导致代码膨胀问题,也会导致编译时间变长。
  • 出现模板编译错误时,错误信息非常杂乱,不易定位错误。

到此这篇关于C++ 深入浅出探索模板的文章就介绍到这了,更多相关C++ 模板内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-04-15

C++ 类模板、函数模板全特化、偏特化的使用

一.类模板全特化.偏特化 #pragma once #include <iostream> #include <map> template <typename T, typename U> class TC { public: TC() { std::cout << "泛化版本构造函数" << std::endl; } void funtest() { std::cout << "泛化版本成员函数&quo

C++模板基础之函数模板与类模板实例详解

泛型编程  如果让你编写一个函数,用于两个数的交换.在C语言中,我们会用如下方法: // 交换两个整型 void Swapi(int* p1, int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } // 交换两个双精度浮点型 void Swapd(double* p1, double* p2) { double tmp = *p1; *p1 = *p2; *p2 = tmp; }  因为C语言不支持函数重载,所以用于交换不同类型变量的函数的函数名是不

C++函数模板与类模板实例解析

本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解.具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思.泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库). 模板(template)是泛型编程的基础,一个模板就是一个创建类或函数的蓝图或公式.例如,当使用一个vector这样的泛型类型或者find这样的泛型函数

C++ 函数模板和类模板详情

目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式化 2.3 函数模板原理 2.4 函数模板实例化 2.5 模板参数的匹配原理 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数? 在C++中可以用到函数重载 class A { public: void Swap(int& x1,int& x2) { int temp=x1; x1=x2; x2=temp; } void Swap(double&

C++类模板与模板类深入详解

1.在c++的Template中很多地方都用到了typename与class这两个关键字,而且有时候二者可以替换,那么是不是这两个关键字完全一样呢? 事实上class用于定义类,在模板引入c++后,最初定义模板的方法为:template<class T>,这里class关键字表明T是一个类型,后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字,它的作用同class一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式了:      t

C++中的类模板详解及示例

C++中的函数模板 对于类的声明来说,也有同样的问题.有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类: 复制代码 代码如下: class Compare_int{ public:  Compare(int a,int b)  {   x=a;   y=b;  }   int max()  {   return (x>y)?x:y;  }  int min()  {   return (x<y)?x:y;  } private:  int x,y;}; 其作用是

PHP模板解析类实例

本文实例讲述了PHP模板解析类.分享给大家供大家参考.具体如下: <?php class template { private $vars = array(); private $conf = ''; private $tpl_name = 'index'; //如果模板不存在 会查找当前 controller默认index模板 private $tpl_suffix = '.html';//如果CONFIG没配置默认后缀 则显示 private $tpl_compile_suffix= '.t

C++堆栈类模板实现代码

最近在复习数据结构,涉及到堆栈的实现,通过类模板可以使堆栈的存储数据类型更为灵活,下面是堆栈的实现代码: #ifndef MYSTACK_H #define MYSTACK_H #include <iostream> using namespace std; template <typename T> class MyStack { public: MyStack(int size); ~MyStack(); bool stackEmpty();//判空 bool stackFul

C++单例类模板详解

单例类 描述 指在整个系统生命期中,一个类最多只能有一个实例(instance)存在,使得该实例的唯一性(实例是指一个对象指针)  , 比如:统计在线人数 在单例类里,又分为了懒汉式和饿汉式,它们的区别在于创建实例的时间不同: 懒汉式 : 指代码运行后,实例并不存在,只有当需要时,才去创建实例(适用于单线程) 饿汉式 : 指代码一运行,实例已经存在,当时需要时,直接去调用即可(适用于多线程) 用法 将构造函数的访问属性设置为private, 提供一个GetInstance()静态成员函数,只能供

c++中的单例类模板的实现方法详解

 1.什么是单例模式 在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在 ( Single Instance ).如超市收银系统,其外观主要由显示器(1个).扫描枪(1个).收款箱(1个)组成,在系统正常运行期间这三部分都是唯一存在的:也就是说,显示器.扫描枪.收款箱这三部分都应该有各自的类,并且每个类只能唯一地实例化一个对象,基于这种模式的程序设计,称为单例模式. !!!单例模式只能创建一个对象,且该对象的生命周期伴随系统的整个运行期间. 2.怎么实现单例模式 思考:如何定义一个类

自定义一个异常类模板的简单实例

一:自定义异常类: package 自定义异常; //或者继承RuntimeException(运行时异常) public class MyException extends Exception { private static final long serialVersionUID = 1L; // 提供无参数的构造方法 public MyException() { } // 提供一个有参数的构造方法 public MyException(String message) { super(mes

C++利用静态成员或类模板构建链表的方法讲解

直接上代码了,说明看注释就可以: 利用静态成员构建链表 #include <IOSTREAM.H> class Node { public: Node(int val, Node* next):val(val),next(next){} //~Node(){cout<<"del "<<val<<endl;} static void showAll();//打印全部节点的值 static void insertHead(int);//头插