C语言的模板与泛型编程你了解吗

目录
  • 模板与泛型编程浅谈
    • 摘要(Effective C++):
    • 模板与泛型编程简单介绍
    • 函数模板
    • 模板编译
    • 类模板
    • 为什么我们需要模板特例化?
  • 总结

模板与泛型编程浅谈

摘要(Effective C++):

​ C++template的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map。然而当愈多人用上templates时,他们发现template有能力完成愈多可能的变化。容器当然很好,但泛型编程(generic programming)——写出的代码和其所处理的对象类型彼此独立——更好。STL算法如for_each,find和merge就是这一类编程的结果。最终人们发现,C++template机制自身是一部完整的图灵机:它可以用来计算任何可计算的值。于是导出了模板元编程(template mataprogramming),创造出“在C++编译器内执行并于编译完成时停止执行”的程序。

模板与泛型编程简单介绍

​ 面向对象编程(OOP)和泛型编程都可以处理编写程序时不知道类型的情况;二者的不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而在泛型编程中,在编译时就能获知类型了

​ 我们所常用的STL标准库中,每一个容器都提供了单一的,泛型的定义,例如我们所常用的vector,我们可以定义很多类型的vector

vector<int> vi; // vi是装载int类型的vector容器的实例
vector<string> vs; // vs是装载string类型的vector容器的实例
vector<double> vd; // vd是装载double类型的vector容器的实例

模板是泛型编程的基础,一个模板就是一个创建类或者函数的蓝图或者公式

函数模板

// 简单的比较函数模板
template<typename T>
int cmp(const T& v1,const T& v2) {
    if(v1<v2)
        return -1;
    else if(v1>v2)
        return 1;
    else
        return 0;
}

函数定义以关键字template开始,后跟一个模板参数列表,这是一个逗号分隔的一个或多个模板参数的列表,用尖括号包围起来

**注:**在模板定义中,模板参数列表不能为空

模板参数列表表示在类或函数定义中用到的类型或者值。当我们使用模板的时候,我们可以(显式或隐式地)指定模板实参,将其绑定到模板参数上

简单了解模板的实例化过程

​ 众所周知,当你觉得模板编程十分智能的时候,一定是有东西在为你负重前行,C++提供了模板与泛型编程的这个能力,这便意味着有一个东西在为你动态地实现模板的功能,而这一定是比C++这个高级语言层面更为底层的东西,而我们所了解的知识中,比C++高级语言较为底层的东西,除了操作系统,便是编译器了。

​ 当我们调用一个函数模板的时候,编译器(通常)用函数实参来为我们推断模板实参。简单来讲,便是我们在调用函数模板的时候,编译器通过使用实参的类型来确定绑定到模板参数T的类型

cout<<cmp(1,0)<<endl; // T为int

在上诉代码中,函数cmp的实参类型是int,编译器便会推断出模板实参为int,并将它绑定到模板参数T上

简单来说,编译器用推断出的模板参数来为我们实例化一个特定版本的函数

模板编译

当编译器遇到一个模板定义的时候,它并不会生成代码。只有我们实例化出模板的一个特定的版本时,编译器才会生成其对应的代码。当我们使用(而不是定义)模板时,编译器才会生成代码。这个特性影响我们如何组织代码以及错误何时才可以被检测到

通常来说,我们将类定义和函数说明放在头文件中,而普通函数和类的成员函数的定义放在源文件中

模板则不尽相同:为了生成一个实例化的版本,编译器需要掌握函数模板或类模板成员函数的定义

总结与非模板代码不同,模板的头文件通常既包括声明也包括定义即函数模板和类模板成员函数的定义通常放在头文件中

大多数编译错误出现的时机 

  • 第一阶段,编译模板本身时,该时期所出现的错误大多数为语法错误
  • 第二阶段,编译器遇到模板使用时
  • 第三阶段,模板实例化时,而只有在这个阶段才能发现类型相关的问题

**注意事项:**保证传递给模板的实参支持模板所要求的操作,以及这些操作在模板中能正确的工作,是调用者的责任

类模板

​ 类模板是用来生成类的蓝图的。与函数模板不同之处是,编译器不能为类模板推断模板参数类型。 所以我们必须在模板名后的尖括号中提供额外的信息——用来替代模板参数的模板实参列表

vector<int> vi;
deque<double> dd;
pair<string,int> key_val;

定义类模板

template<typename T>
class T_vector {
public:
	typedef T value_type;
    // 构造函数
    T_vector() =default;
    T_vectot(std::initializer_list<T> il);
    // 容器的元素数目
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    // 添加元素
    void push_back(const T& val) {
        data->push_back(val);
    }
    void push_back(T &&val) {
        data->push_back(std::move(val));
    }
private:
    std::shared_ptr<std::vector<T> > data;
    // 若data[i]无效,则抛出msg
    void check(size_type i,const std::string &msg) const;
}

类似函数模板,类模板以关键字template开始,后跟模板参数列表。在类模板(及其成员)的定义中,我们将模板参数当作替身,代替使用模板时用户需要提供的类型或值

**注:**一个类模板的每一个实例都形成一个独立的类,而类模板的每个实例都有其自己版本的成员函数

​ 所以,我们可能会出现一个单一模板并不能满足所有类型的需求,而模板特例化就出现了

类模板成员函数的实例化

​ 默认的情况下,一个类模板的成员函数只有在程序用到它的时候才会实例化

// 实例化T_vector和接受initializer_list<int>的构造函数
T_vector<int> T_vi = { 0,1,2,3,4,5 };

如果一个成员函数没有被使用,则它将不会被实例化

为什么我们需要模板特例化?

当我们编写单一的模板时,使其对任何可能的模板实参都是最合适的,都能实例化,但者往往都是过于理想化的情况。在某些特殊的情况下,通用的模板的定义可能对特定的类型是不合适的,通用定义的模板可能会出现编译失败或者做得不够完善的情况。

​ 故,当我们不能(或者不希望)使用模板版本的时候,可以定义类或函数模板的一个特例化版本

定义函数模板特例化

// 原先cmp函数的特殊版本,用来处理特殊的字符数组的指针template<>int cmp(const char* const& p1,const char* const& p2) {    return strcmp(p1,p2);}// 原先cmp函数的特殊版本,用来处理特殊的字符数组的指针
template<>
int cmp(const char* const& p1,const char* const& p2) {
    return strcmp(p1,p2);
}

函数重载与模板特例化的区别

​ 当定义函数模板的特例化版本时,我们本质上接管了编译器的工作。即,我们为原先的模板的其中一个特殊的实例提供了定义。简而言之,特例化的本质是实例化一个模板,而非重载它,因此特例化并不影响函数匹配

注意事项:

  • 为了特例化一个模板,原模版的声明必须在作用域中
  • 在任何使用模板实例的代码之前,特例化版本的声明也必须在作用域中
  • 所有同名模板的声明应该放在前面,然后是这些模板的特例化版本

类模板部分特例化

与函数模板不同的是,类模板的特例化不必为所有模板参数提供实参。一个类模板的部分特例化本身是一个模板,使用它时用户还必须为那些在特例化版本中指定的模板参数提供实参

我们只能部分特例化类模板,而不能部分特例化函数模板

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C语言利用模板实现简单的栈类

    本文实例为大家分享了C语言利用模板实现简单的栈类(数组和单链表),供大家参考,具体内容如下 主要的功能是实现一个后进先出的列表,有入栈.出栈.返回大小.判空等基本功能 #pragma once using namespace std; const int MAXSIZE = 0xfff; template<class type> class Class_Linkstack { int top; type* my_s; int max_size; public: Class_Linkstack(

  • C语言实现全排列算法模板的方法

    程序的主要思路是: 1.把第1个数换到最前面来(本来就在最前面),准备打印1xx,再对后两个数2和3做全排列. 2.把第2个数换到最前面来,准备打印2xx,再对后两个数1和3做全排列. 3.把第3个数换到最前面来,准备打印3xx,再对后两个数1和2做全排列. 可见这是一个递归的过程,把对整个序列做全排列的问题归结为对它的子序列做全排列的问题,注意我没有描述Base Case怎么处理,你需要自己想.你的程序要具有通用性,如果改变了N和数组a的定义(比如改成4个数的数组),其它代码不需要修改就可以做

  • C语言泛型编程实例教程

    本文实例讲述了C语言泛型编程的方法,分享给大家供大家参考之用.具体分析如下: 首先,泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同.在C语言中,可以通过一些手段实现这样的泛型编程.这里介绍一种方法--通过无类型指针void* 看下面的一个实现交换两个元素内容的函数swap,以整型int为例: void swap(int* i1,int* i2){ int temp; temp = *i1; *i1 = *i2; *i2 = temp; } 当你想交换两个

  • C语言数组栈实现模板

    本文实例为大家分享了C语言数组栈实现模板的具体代码,供大家参考,具体内容如下 SeqStack.h #pragma once #define MAX_SIZE 1024 typedef struct SEQSTACK { void* data[MAX_SIZE]; int size; }SeqStack; SeqStack* Init_SeqStack(); // 初始化栈 void Push_SeqStack(SeqStack* stack, void* data); // 入栈 void*

  • C语言的模板与泛型编程你了解吗

    目录 模板与泛型编程浅谈 摘要(Effective C++): 模板与泛型编程简单介绍 函数模板 模板编译 类模板 为什么我们需要模板特例化? 总结 模板与泛型编程浅谈 摘要(Effective C++): ​ C++template的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map.然而当愈多人用上templates时,他们发现template有能力完成愈多可能的变化.容器当然很好,但泛型编程(generic programming)——写出的代码和其所处理

  • Go语言基础模板设计模式示例详解

    目录 概述 模板模式生活案例 策略模式涉及到两个角色 UML 总结 示例 概述 模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式.让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤 确定了步骤的执行顺序,单某些步骤因环境或人等因素具体实现是未知的 模板模式生活案例 请客吃饭[点菜->吃东西->结账],每个人点菜不一样,吃东西不一样,结账也不一样从某地到某地[起点->出行方式->终点]起点和终点不一一样,但是每个人出行方式是不一样的 Go没有封装.

  • 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语言不支持函数重载,所以用于交换不同类型变量的函数的函数名是不

  • Django框架模板文件使用及模板文件加载顺序分析

    本文实例讲述了Django框架模板文件使用及模板文件加载顺序.分享给大家供大家参考,具体如下: 模板功能 产生html,控制页面上产生的内容.模板文件不仅仅是一个html文件. 模板文件包含两部分内容: 1.静态文件:css,js,html 2.动态内容:用于动态的去产生一些网页内容,通过模板语言产生 模板文件的使用 通常是在视图函数中使用模板产生html内容返回给客户端 a,加载模板文件 loader.get_template 获取模板文件的内容,产生一个模板对象 b,定义模板上下文 Requ

  • 易语言插件按键精灵调用方法

    给按键精灵写插件的方法有很多,例如vc,vb,乃至delphi.但是,使用这些程序语言给按键精灵写插件,对编写者的要求比较高.易语言呢,编程门槛比较低,也有很多开源不开源的模块,但是在8.2版本之前,按键精灵是不支持易语言编写插件的.当然,现在就没有这个问题了.今天我就教大家怎么用易语言给按键精灵写一个简单的插件. 1.至于安装按键精灵和易语言,相信大家都会的,这里主要讲怎么写插件,安装步骤就暂时略过.首先找到按键精灵的安装目录,找到source文件夹打开 2.找到 QMPlugin插件制作模版

  • C++ 泛型编程详解

    泛型编程与面向对象编程的目标相同,即使重用代码和抽象通用概念的技术更加简单.但是面向对象编程强调编程的数据方面,泛型编程强调的是独立于特定数据类型. 这一篇介绍一下 C++ 编程中与面向对象并列的另一大分支--泛型编程,这一篇主要介绍函数模板.类模板和成员模板三大部分 如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢 泛型编程 模板是泛型编程的一种重要思想,STL(Standard Template Library,标准模板库)是采用模板实现的一个实例 函数模板 对比函数重载(同一作用域内函数

  • C++泛型编程基本概念详解

    目录 1.什么是泛型编程? 2.函数模板 (1)函数模板概念 (2)函数模板格式 (3)函数模板的原理 (4)函数模板的实例化 (5)模板参数的匹配原则 3.类模板 (1)类模板的定义格式 (2)类模板的实例化 总结 1.什么是泛型编程? 比如说,我们如何实现一个通用的交换函数呢?int型.double型.char型的交换 void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } vo

  • C++模板Template详解及其作用介绍

    目录 1. 模板 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 2.6声明定义分离 3. 类模板 3.1 类模板格式 3.2 类模板的实例化 3.3 类模板中函数放在类外进行定义时 4. 模板分离编译 4.1 什么是分离编译 4.2 模板的分离编译 5. 缺省值与返回值 6. 总结 1. 模板 首先模板分为函数模板和类模板 想到模板,就会联想到泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手

  • C++模板全方位深入解读

    目录 1.泛型编程 2.函数模板 概念 函数模板的格式 函数模板的原理 函数模板的实例化 隐式实例化 显式实例化 模板参数的匹配原则 3.类模板 (1) 类模板的定义格式 (2) 类模板的实例化 4.非类型模板参数 5.模板特化 (1)函数模板的特化 (2)类模板的特化 全特化 偏特化 6.模板的分离编译 问题分析 1.泛型编程 如何实现一个通用的交换函数? 这点函数重载可以做到,比如一下Swap函数的重载,分别重载了俩种不同参数类型的Swap void Swap(int& x, int&

随机推荐