首页 > 编程语言 >模板编程

模板编程

时间:2024-01-17 17:06:14浏览次数:36  
标签:函数 编程 编译器 实例 类型 模板 特化

函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当他具体执行的时候,将根据传递的实际参数决定其功能(运行期间的多态,动态多态)。C++提供两种模板机制:类模板和函数模板。

注意这里T类型必须在使用模板的时候定义,而且可以有多个T类型,这里的typename可以改为class,意味一个通用类,两种写法相同。

template<typename T…>//函数或类的声明或定义

如果在模板类中想要声明一个模板方法,可以直接使用定义类时定义的typename T。

普通函数与函数模板的区别
  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
模板函数的特点
  • 模板函数也可以被重载
  • C++编译器在调用的时候会优先考虑普通函数
  • 如果函数模板可以产生一个更好的匹配,那么就选择模板(特化)
  • 在项目中定义函数模板时,声明和定义必须在同一个文件里。
泛型编程和模板元编程

所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议

元编程侧重点在于「用代码生成代码」,泛型编程侧重点在于「减小代码对特定数据类型的依赖」。而这两个功能在C++中,模板功能可以实现。

模板的全特化和偏特化

模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。

模板分为类模板与函数模板,特化分为全特化与偏特化。全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。

特化并不是完全取代原来的模板,如果传入的参数不满足特化类型的参数的话,还是会调用之前的模板函数

编译器不支持函数模板的偏特化,原因很简单,因为已经有模板函数的重载可以实现这种功能了。

注意在尖括号内规定类型才叫做特化,在参数列表中规定类型叫做重载

template<typename T> 
class C<int T>{}             //特化
template<typename T> 
void f(int a){}                //重载

为什么模板的定义和声明必须在一起

假设有代码段如下

//-----------------text.h---------------//
void f();//声明一个函数
//-----------------test.cpp-----------//
#include "test.h"
void f(){}
//-----------------main.cpp----------//
#include"test.h"
int main(){
f();
}

在这个例子中

编译main.cpp时,编译器不知道f的实现,所以当碰到对它的调用时只是给出一个指示,指示连接器应该为它寻找f的实现体。这也就是说main.obj(可执行文件)中没有关于f的任何一行二进制代码。

编译test.cpp时,编译器找到了f的实现。于是乎f的实现(二进制代码)出现在test.obj里。

连接时,连接器在test.obj中找到f的实现代码(二进制)的地址(通过符号导出表)。然后将main.obj中悬而未决的调用函数f的部分的地址改成f实际的地址。完成。

对于模板函数来说,模板函数的代码并不能直接编译成二进制代码(运行时编译),其中要有一个实例化的过程。

在上述例子中,如果f是一个模板函数且main.cpp中没有调用过,f就得不到实例化,从而main.obj中就没有f的任意一行二进制代码。而当我们调用乐f<double>(1.1),f<int>(1)时,f<double>和f<int>两个函数的二进制代码段就会生成在main.obj中

而实例化要求编译器知道模板的定义

在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来,所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码,于是连接器也黔驴技穷了。

模板的优缺点

优点:

1. 灵活性, 可重用性和可扩展性;

2. 可以大大减少开发时间,模板可以把用同一个算法去适用于不同类型数据,在编译时确定具体的数据类型;

3. 模版模拟多态要比C++类继承实现多态效率要高, 无虚函数, 无继承;

缺点:

1. 易读性比较不好,调试比较困难;

2. 模板的数据类型只能在编译时才能被确定;

3. 所有用基于模板算法的实现必须包含在整个设计的.h头文件中, 当工程比较大的时候, 编译时间较长;

4.如果模板中传入的typename是函数指针类型,那么会导致模板效率很低。并且他会在每次实例化的时候,如果传入的方式不同(lambda表达式,函数指针等方式),都生成一个新的对象。

另外:

up-cast

"upcast"是一个术语,指的是将一个子类的实例转换为它的父类类型的一个过程。这种转换是安全的,因为子类继承了父类的所有属性和方法,所以它保证了父类类型的所有操作在子类实例上都是合法的。

在面向对象编程中,通常有继承层次结构,子类继承父类。当有一个子类对象,并且我们将它赋值给父类类型的引用或变量时,这个操作就是upcasting。由于子类具有父类的所有功能,所以向上转型通常是隐式的和自动的。

成员模板 函数模板 类模板
成员模板

在C++模板编程中,除了可以创建整个类的模板,还可以创建类中单独成员函数的模板,这种成员函数模板称为成员模板(Member templates)。成员模板可以在普通类中定义,也可以在类模板中定义。

当在普通类(非模板类)中定义时,成员模板通常用来定义那些能够处理不同类型参数的成员函数,而不必针对这个类本身使用模板。

普通类中的成员模板示例:

class MyClass {
public:
    // 成员模板函数
    template <typename T>
    void doSomething(T param) {
        // 处理param...
    }
};

在这个例子中,虽然 MyClass 不是模板类,它包含了一个模板成员函数(成员模板)doSomething,这个函数可以接受任意类型的参数。

如果成员模板是在类模板中定义的,它会提供更多针对不同类型操作的灵活性。这意味着即使类的实例化类型已经确定,成员模板仍然可以接收和处理不同类型的参数。

类模板中的成员模板示例:

template <typename T>
class MyTemplateClass {
public:
    // 构造函数
    MyTemplateClass(T value) : data(value) {}

    // 成员模板函数,可以接受与类模板参数不同的类型
    template <typename U>
    void doSomething(U param) {
        // 处理param和data...
    }

private:
    T data;
};

// 使用示例
int main() {
    MyTemplateClass<int> myInstance(10);
    // 调用成员模板函数,使用与类实例化类型不同的类型参数
    myInstance.doSomething("Hello world");
}

在这个例子中,即使 MyTemplateClass 被实例化为 int 类型,成员模板函数 doSomething 仍然可以被调用,并处理一个 const char* 类型的字符串。

成员模板在编写泛化的代码时非常有用,它们可以增加类的灵活性,提供处理不同类型数据的能力,无论这些数据是否与类的模板类型参数相同。这对于编写如容器类、算法等泛型编程的库非常重要,比如STL中就广泛使用了成员模板。

函数模板

C++模板编程是一种基于泛型的编程范式,它允许程序员编写与数据类型无关的代码。模板可用于函数模板和类模板。

函数模板 允许我们创建一个函数原型,其中一个或多个参数类型是泛型的。当我们使用不同的数据类型调用该函数时,编译器会为我们自动生成相应类型的函数实例。

例如,一个简单的交换函数模板可能是这样的:

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

在该例中,typename T 是一个模板参数,代表了将要使用这个函数的任意类型。swap 函数可以用于任何数据类型,例如 int、double 或者自定义的类。

类模板

类模板 工作原理相似,但它们用于创建泛型的数据结构或类。

例如,一个简单的类模板用于创建一个数组容器可能是这样的:

template <typename T>
class Array {
private:
    T* data;
    int size;
public:
    Array(int size) : size(size) {
        data = new T[size];
    }
    ~Array() {
        delete[] data;
    }
    void set(int index, T value) {
        if (index >= 0 && index < size) {
            data[index] = value;
        }
    }
    T get(int index) {
        if (index >= 0 && index < size) {
            return data[index];
        }
        return T(); // 返回类型的默认值
    }
};

在上述例子中,我们定义了一个Array模板,它可以存储任何给定类型T的元素数组。

在实际使用中,模板编程可以提高代码复用性和灵活性,但同时也会增加编译器的编译时间,因为需要为不同类型生成不同的实例。此外,模板错误有时可能难以调试,因为它们可能产生冗长且难以理解的错误信息。

理解模板的基本概念对于理解和使用C++的STL(Standard Template Library)至关重要,因为STL广泛使用了模板来实现各种通用、高效的数据结构和算法。

标签:函数,编程,编译器,实例,类型,模板,特化
From: https://blog.51cto.com/u_15958702/9294021

相关文章

  • 在 SAP Web IDE 个人版中根据模板创建项目时,选择 OData 服务时出现catalog service is
     1.NOTE2684697 ,重点是点2点5的问题 2.去掉CatalogServiceVersion2的系统别名(包括LOCAL)翻阅其他博客,有人说是因为系统别名,我给去掉了。 ......
  • 二次剩余模板简记
    \(x^2\equivn\pmodp\),其中\(p\)是奇素数。当\(n=0\)时有\(x=0\),以下规定\(n\not=0\)。假设\(n\)是二次剩余且有多个不同解,其中两个解分别是\(x_0,x_1\in[1,p)\)。有\({x_0}^2\equiv{x_1}^2\equivn\pmodp\)。移项,平方差公式得\((x_0+x_1)(x_0-x_1)\equiv0\p......
  • C++ STL标准模板库
    目录简介容器(Containers)迭代器(Iterators)算法(Algorithms)函数对象(FunctionObjects)适配器(Adaptors)分配器(Allocators)std::min_element()简介C++中的STL(标准模板库)可以分为六个部分,分别是容器(Containers)、迭代器(Iterators)、算法(Algorithms)、函数对象(FunctionObjects)、适配器(Adapto......
  • ASP.NET Core 中AOP(面向切面编程)的支持方式
    在ASP.NETCore中,AOP(面向切面编程)的支持可以通过以下方式实现:过滤器(Filters):ASP.NETCore提供了多种类型的过滤器:身份验证过滤器(AuthenticationFilters):用于验证用户身份。例如,[Authorize] 属性可以应用在控制器或动作方法上,确保只有经过身份验证的用户才能访问。授权......
  • C语言爬虫程序编写的爬取APP通用模板
    互联网的飞快发展,尤其是手机终端业务的发展,让越来越多的事情都能通过手机来完成,电脑大部分的功能也都能通过手机实现,今天我就用C语言写一个手机APP类爬虫教程,方便后期拓展APP爬虫业务。而且这个模板是通用的适合各种APP爬虫,下面跟着我看下具体的代码吧。下面就是我给大家提供一个基......
  • 使用 Python 创造你自己的计算机游戏(游戏编程快速上手)第四版:第十五章到第十八章
    十五、反转棋游戏原文:inventwithpython.com/invent4thed/chapter15.html译者:飞龙协议:CCBY-NC-SA4.0在本章中,我们将制作反转棋,也称为黑白棋或奥赛罗。这个双人棋盘游戏是在网格上进行的,因此我们将使用带有x和y坐标的笛卡尔坐标系。我们的游戏版本将具有比第10章中的......
  • 使用 Python 创造你自己的计算机游戏(游戏编程快速上手)第四版:第十九章到第二十一章
    十九、碰撞检测原文:inventwithpython.com/invent4thed/chapter19.html译者:飞龙协议:CCBY-NC-SA4.0碰撞检测涉及确定屏幕上的两个物体何时相互接触(即发生碰撞)。碰撞检测对于游戏非常有用。例如,如果玩家触碰到敌人,他们可能会失去生命值。或者如果玩家触碰到硬币,他们应该自动......
  • 使用 Python 创造你自己的计算机游戏(游戏编程快速上手)第四版:第五章到第九章
    五、龙之境原文:inventwithpython.com/invent4thed/chapter5.html译者:飞龙协议:CCBY-NC-SA4.0本章中您将创建的游戏名为龙之境。玩家需要在两个洞穴之间做出选择,这两个洞穴分别藏有宝藏和一定的厄运。如何玩龙之境在这个游戏中,玩家身处一个充满龙的土地。这些龙都住在洞......
  • 模型组成的编程世界
    现实世界中的树,它被人起名叫做树,就是人给这种特征的集合做了一个模型,并给其命名为树。编程的世界中也有很多的专有名词,这就是程序员先辈们给模型命的名。吸收二氧化碳,阳光催化,生根于土地————>树各种特征的集合————>模型模型的存在一定程度上减少了记忆的复杂程度......
  • 使用 Python 创造你自己的计算机游戏(游戏编程快速上手)第四版:第十章到第十四章
    十、井字棋原文:inventwithpython.com/invent4thed/chapter10.html译者:飞龙协议:CCBY-NC-SA4.0本章介绍了一个井字棋游戏。井字棋通常由两个人玩。一个玩家是X,另一个玩家是O。玩家轮流放置他们的X或O。如果一个玩家在一行、一列或对角线上获得了三个标记,他们就赢了。当棋盘......