【类模板】成员函数模板

news2024/9/22 21:56:22

一、成员函数模板的基本含义

不管是普通的类,还是类模板,都可以为其定义成员函数模板,以下的情况就是类模板和成员函数模板都有各自独立的一套参数:


template<typename T1>
class A {
public:

    T1 m_ic;
    static constexpr int m_stacvalue = 200;

public:
    
    //构造函数模板
    template<typename T2>
    A(T2 v1, T2 v2); //这里的T2和T1没有关系
  

    //普通成员函数模板
    template<typename T3>
    void myft(T3 tmpt) {
        std::cout << tmpt << "\n";
    }


    //拷贝构造函数模板
    template<typename U>
    A(const A<U>& other) {
        std::cout << "A::A(const A<U>& other)调用了!\n";
    }

    //拷贝赋值运算符模板
    template<typename U>
    A<T1>& operator=(const A<U>& other) {
        std::cout << "operator=(const A<U>& other)调用了!\n";
        return *this;
    }

};

这里我们为以下的成员提供了模板:

1.赋值构造函数
2.拷贝构造函数
3.赋值运算符
4.普通成员函数

二、构造函数模板与赋值运算符模板

1.构造函数模板

1.1 赋值构造函数模板

先观察下面三个赋值构造函数(模板):

 //构造函数模板
 template<typename T2>
 A(T2 v1, T2 v2); //这里的T2和T1没有关系
 
 //添加一个double类型的构造函数
 A(double v1, double v2) {
     std::cout << "A::A(double,double)运行了!\n";
 }

 //添加一个T1的类型的构造函数
	A(T1 v1, T1 v2) {
		std::cout << "A::A(T1,T1)运行了!\n";
	}

其中第一个函数我们放在类外实现,注意参数列表的声明顺序要与类内的顺序保持一致:

//类外实现类模板的构造函数模板
template<typename T1>
template<typename T2>
A<T1>::A(T2 v1, T2 v2) {
    std::cout << "A::A(T2,T2)运行了!\n";
}

当我们运行:

void Test1() {
    A<int> a1(1, 1); //T1为int,T2为int
    a1.myft(1.0f); //T3为float

    A <int> a2(1.0, 1.0); //T1为 int,T2为double
    a2.myft('a'); //T3为char类型

    A<float>a3(1,1); //T1为float类型,T2为int类型
    a3.myft(1.0f); //T3为float;
}

可以发现调用的优先级:
一定是:
A ( d o u b l e , d o u b l e ) > A ( T 1 , T 1 ) > A ( T 2 , T 2 ) A(double,double)>A(T1,T1)>A(T2,T2) A(double,double)>A(T1,T1)>A(T2,T2)
只有前者不存在才会是有后者的模板来实例化。

运行后得到:

在这里插入图片描述
结果和预期一样。

1.2拷贝构造函数模板

观察下面的拷贝构造函数模板:

  //拷贝构造运算符模板
  template<typename U>
  A(const A<U>& other) {
      std::cout << "A::A(const A<U>& other)调用了!\n";
  }

注意,我们的类内没有定义拷贝构造函数,只有拷贝构造函数模板,那么当我们执行下面这行代码时,是否能成功调用拷贝构造函数模板呢?

 A<int>a1(0, 0);
 a1.m_ic = 1;

 A<int>a2(a1); //实际上不会执行拷贝构造函数模板

 std::cout << a2.m_ic << "\n"; //确实发生拷贝了

运行后发现,只调用了一开始的赋值构造函数模板,并没有调用拷贝构造函数模板。

但却神奇的发现,确实发生了拷贝,即使不存在拷贝构造函数!
在这里插入图片描述

实际上这里,可以理解为编译器隐式地生成一个拷贝构造函数帮助我们拷贝(更多细节请自行查找资料)。

我们补充上拷贝构造函数看看:

 A(const A<T1>& other) {
     std::cout << "A::A(const A<T1>& other)调用了!\n";
 }

成功调用了拷贝构造运算符,因为我们没有赋值,所以得到了一个随机值:
在这里插入图片描述

那什么时候会调用拷贝构造函数模板呢?
当拷贝类型不一致的时候!

如下,我们运行后得到:

    A<int>a1(0, 0);
    a1.m_ic = 1;

    A<int>a2(a1); //实际上不会执行拷贝构造函数模板

    std::cout << a2.m_ic << "\n"; //确实发生拷贝了
    
    A<float>a3(a1); //执行拷贝构造函数模板

因为没有赋值,所以还是随机值,但是确实调用了拷贝构造函数模板:

在这里插入图片描述

如果多试试,我们发现去除掉拷贝函数模板中的 c o n s t const const修饰符,就不存在这个限制了:

 template<typename U>
 A(A<U>& other) {
     std::cout << "A::A(const A<U>& other)调用了!\n";
 }

在这里插入图片描述
但是,为了编写的完整性,以及 c o n s t const const修饰符对编译器的影响,还是不建议去除掉这个 c o n s t const const修饰符,而是手动补充一个拷贝构造函数。

记住:拷贝构造函数模板永远不可能是拷贝构造函数,即使拷贝构造函数不存在!

2.赋值运算符模板

对于赋值运算符模板和拷贝构造函数模板基本一致,对于相同的类型,调用的永远是赋值运算符,如果不存在,可以看做系统会默认生成一个。

而对于不同的类型,会调用赋值运算符模板。

同样的,去除掉 c o n s t const const关键字能起到同样的作用,但不建议使用!

下面是具体的代码和运行结果:


    //拷贝赋值运算符模板
    template<typename U>
    A<T1>& operator=(const A<U>& other) {
        std::cout << "operator=(const A<U>& other)调用了!\n";
        return *this;
    }
#if 0 
    //去掉const限定
    template<typename U>
    A<T1>& operator=(A<U>& other) {
        std::cout << "operator=( A<U>& other)调用了!\n";
        return *this;
    } 
 #endif    
    //拷贝运算符
    A<T1>& operator=(const A<T1>& other) {
        std::cout << "operator=(const A<T1>& other)调用了!\n";
        return *this;
    }

测试代码:

 //与拷贝构造函数模板类似
 a2 = a1;
 std::cout << a2.m_ic << "\n"; //确实发生拷贝了

 a3 = a1;

运行结果:
在这里插入图片描述

三、成员函数模板

首先,我们在类内实现了三个成员函数模板,分别是泛化、偏特化和全特化

 //泛化版本
 template<typename T3,typename T4>
 void myft2(T3 tmpt1,T4 tmpt2) {
     std::cout << "调用了myft2()的泛化版本\n";
     std::cout << tmpt1 << "\n";
     std::cout << tmpt2 << "\n";
 }

 //偏特化
 template<typename T4>
 void myft2(int tmpt1, T4 tmpt2) {
     std::cout << "调用了myft2(int,T4)偏特化版本\n";
     std::cout << tmpt1 << "\n";
     std::cout << tmpt2 << "\n";
 }

 //全特化
 template<>
 void myft2(double tmpt1,double tmpt2) {
     std::cout << "调用了myft2(double,double)全特化版本\n";
     std::cout << tmpt1 << "\n";
     std::cout << tmpt2 << "\n";
 }

调用优先级仍然是:
普通函数 > 全特化 > 偏特化 > 泛化 普通函数>全特化>偏特化>泛化 普通函数>全特化>偏特化>泛化

值得注意的是,如果我们在类内声明,而在类外实现成员函数的时候,全特化是无法正常实现的,如下:

//类外实现类模板的构造函数模板
template<typename T1>
template<typename T2>
A<T1>::A(T2 v1, T2 v2) {
    std::cout << "A::A(T2,T2)运行了!\n";
}


//泛化
template<typename T1>
template<typename T2,typename T3>
void A<T1>::myft3(T2 tmpt1, T3 tmpt2) {
    std::cout << "调用了myft3()的泛化版本\n";
    std::cout << tmpt1 << "\n";
    std::cout << tmpt2 << "\n";
}

//偏特化
template<typename T1>
template<typename T3>
void A<T1>::myft3(int tmpt1, T3 tmpt2) {
    std::cout << "调用了myft3()的偏特化版本\n";
    std::cout << tmpt1 << "\n";
    std::cout << tmpt2 << "\n";
}

//全特化
template<typename T1>
template<> //注意这里会失败
void A<T1>::myft3(double tmpt1, double tmpt2) {
    std::cout << "调用了myft3()的全特化版本\n";
    std::cout << tmpt1 << "\n";
    std::cout << tmpt2 << "\n";
}

注意,全特化版本这样会编译失败:
在这里插入图片描述

一种解决方法是在类内写一个普通的函数作为特化版本,在类外补充的时候可以去除 t e m p l a t e < > template<> template<>,这样就可以成功编译了。

另一种解决方法是对整个类进行全特化,然后只修改成员函数部分的内容,其他内容同泛化版本,如下:

template<>
class A<double> {
public:
    
    template<typename T4>
    A(T4 v1,T4 v2) {
        std::cout << "调用了A<double>::A(double,double)的构造函数\n";
    }

    template<typename T2,typename T3>
    void myft3(T2 tmpt1, T3 tmpt2) {
        std::cout << "调用了myft3()的泛化版本\n";
        std::cout << tmpt1 << "\n";
        std::cout << tmpt2 << "\n";
    }
    //剩下的和之前的函数一致,省略
    //...

    //...
};

调用以下代码:

void Test3() {
	 A<int>a(1, 1);
	 a.myft3(1.2, 1.2);
	
	 std::cout << std::endl;
	
	 A<double>a2(1.0, 1.0);
	 a2.myft3(1.0,1.0);

}

可以发现成功运行起来了:

在这里插入图片描述

四、虚函数

虚函数是不存在成员函数模板的,原因有很多,其中有一条重要的原因是因为虚函数在编译期间就已经被实例化出来了,并创建了虚函数表 v t b l vtbl vtbl,因此如果要求虚函数可以模板化,那么只能有连接器来实现这个功能,显然这个要求目前是不容易实现的。
更多细节如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101283.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电力104规约

对象性质十进制十六进制数量适用报文类型ASDU遥测1793~2304701H~900H512*9、11、21、34、35遥信1~10241H~400H1024*1、3、20、30、31遥控2817~2944B01H~B80H128*45、46遥调2945~3072B81H~C00 H128*47APCI 应用规约控制信息; ASDU 应用服务数据单元; APDU 应用规约数据单元;…

CountDownLatch的应用与原理

一、什么是CountDownLatch CountDownLatch是具有synchronized机制的一个工具&#xff0c;目的是让一个或者多个线程等待&#xff0c;直到其他线程的一系列操作完成。 CountDownLatch初始化的时候&#xff0c;需要提供一个整形数字&#xff0c;数字代表着线程需要调用countDow…

K8s系列之:解释Kubernetes Operators

K8s系列之&#xff1a;解释Kubernetes Operators 什么是控制器循环&#xff1f;Kubernetes Operator是如何工作的&#xff1f;如何添加自定义资源自定义资源定义Kubernetes Operators&#xff1a;案例研究 你是否曾想过&#xff0c;Site Reliability Engineering&#xff08;SR…

【优化】Nginx 配置页面请求不走缓存 浏览器页面禁用缓存

【优化】Nginx 配置页面请求不走缓存 禁用缓存 目录 【优化】Nginx 配置页面请求不走缓存 禁用缓存 对所有请求禁用缓存 对特定location禁用缓存 注意事项 全局禁用缓存 要配置Nginx使其不缓存内容&#xff0c;通常是指禁止浏览器缓存响应的内容&#xff0c;或者是在代理…

Qt 模仿企业微信图标实现按钮图片文字上下结构

简述 实现类似企业微信左侧导航栏的上下结构的按钮 效果图 可以用2种方案实现&#xff0c;2种最终效果图如下&#xff1a; 方案1 QToolButton 实现 ui.toolButton->setFixedSize(50, 50);ui.toolButton->setCheckable(true);ui.toolButton->setAutoExclusive(true);…

电源测试设备功能篇:测试仪器的灵活兼容与扩展

依托ATECLOUD智能云测试平台打造的电源ate自动测试设备&#xff0c;相较于传统的自动化测试系统&#xff0c;其突出特点在于提供了灵活的系统操作。这种操作灵活性不仅表现在自动化测试的便捷性、报告模板的多样化以及数据分析的灵活性上&#xff0c;还表现在电源测试仪器设备配…

覃嘉仪,艺人经纪人、经纪人、影视经纪人。2002.7.9出生于四川省遂宁市射洪县

覃嘉仪&#xff0c;艺人经纪人、经纪人、影视经纪人。2002.7.9出生于四川省遂宁市射洪县 2020年开始从事宣传工作&#xff0c;2023成为“WP经纪工作室”艺人经纪&#xff0c;现担任孙亦欣、魏逸熙等艺人的经纪人。 2024年涉足于影视行业&#xff0c;并加入嘉林娱乐。2024年在由…

部标JT808标准下的视频汇聚新方案:EasyCVR平台助力推动车辆监管智能化进程

在数字化转型的浪潮中&#xff0c;智慧城市的建设正以前所未有的速度推进&#xff0c;而市政车载设备作为城市运行的重要“神经末梢”&#xff0c;其智能化、联网化水平直接影响着城市管理效率与服务质量。近年来&#xff0c;随着部标协议&#xff08;即国家行业标准协议&#…

Python学习日志(3)—— 运行

通过python文档辅助学习&#xff0c;规范代码 python文档&#xff1a;3.12.5 Documentation (python.org) 1、python版本之间的差异&#xff1a;新的特性和变化、弃用和新增 2、标准库参考&#xff08;宝典&#xff0c;用于查询&#xff09; 3、语法参考 python程序是解释型…

记录工作时的一些错误

1、mobaxterm问题&#xff1a; 解决方案&#xff1a;找不到mottynew.exe 2、虚拟机安装centos7进入不了引导页面 解决方案&#xff1a;检查镜像 虚拟机 192.168.40.128 root/Root yxr/y123x123r123 解决方案&#xff1a; 问题&#xff1a;docker run不起来容器&#xff0c;显…

HTTP 状态码全攻略:快速搞懂服务器的“暗号”

文章目录 HTTP 状态码全攻略&#xff1a;快速搞懂服务器的“暗号”1xx&#xff1a;打个招呼&#xff0c;信息来了 (Informational Responses)2xx&#xff1a;事情办成了&#xff01; (Successful Responses)3xx&#xff1a;走这边&#xff0c;换个地方吧 (Redirection Response…

音视频——RTSP流媒体传输技术介绍及抓包解析

流式传输 流媒体技术&#xff1a;将声音影像向用户计算机 连续、不间断的进行传送&#xff0c;延时小。 抓包 route add 添加到指定网络的路由规则 route add [-net|-host] [网域或主机] netmask [mask] [gw|dev] route del [-net|-host] [网域或主机] netmask [mask] [gw|dev…

第9章 使用ContentProvider实现数据共享

第9章 使用ContentProvider实现数据共享 本章要点 理解ContentProvider的功能与意义ContentProvider类的作用和常用方法Uri 对 ContentProvider的作用理解ContentProvider与ContentResolver的关系实现自己的ContentProvider配置ContentProvider使用ContentResolver操作数据操…

宝藏!《联盟自控基础班筑基题库》(凤凰篇) 1-8章:甄选部分

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;初试《自控基础班筑基题库》(凤凰篇)。 Part1&#xff1a;资料封面&目录 Part2&#xff1a;资料各个章节具体内容 第1章 自动控制的基本概念 第2章 控制系统的数学模型 第3章 控制系统的时域分析 第4章 根轨迹法…

某东东的jdgs算法分析--适合进阶学习

某东东的jdgs算法分析 这个贴主要还是对算法本身结构部分描述会多点&#xff0c;憋问&#xff0c;问就是过去太久了&#xff0c;很多逆向过程不一定能还原&#xff08;主要是懒&#xff0c;不想原路再走一遍&#xff09;&#xff0c;所以可能有部分跳跃的内容&#xff0c;会给具…

【网络安全】IDOR之敏感数据泄露

未经许可,不得转载。 文章目录 正文正文 在测试“添加到收藏夹”功能时,我拦截了发送到服务器的请求,请求体如下: {“uriTemplate”:“asset/{assetId}/favorite”,“version”:“v2”,“type”:“POST”,“req_service”:“pict”,“url”:“asset/VICTIMS_ASS…

【论文阅读】DivTheft: An Ensemble Model Stealing Attack by Divide-and-Conquer(2023)

摘要 Recently, model stealing attacks(模型窃取攻击) are widely studied(广泛研究) but most of them are focused on stealing a single non-discrete model(窃取单个非离散模型), e.g., neural networks(神经网络). For ensemble models(对于集成模型), these …

分钟快速搭建分班查询系统,支持查班级群二维码

新学期已经开始了&#xff0c;老师们又要忙活起来了。但是&#xff0c;别担心&#xff0c;现在有个超方便的工具&#xff0c;能帮大家快速搞定分班的事情&#xff0c;还能让家长们一键加入班级群&#xff0c;省时又省力&#xff01; 以前分班可麻烦了&#xff0c;老师们得一个…

集群的调度和策略

集群的调度&#xff1a; 怎么把pod部署到节点的方法。 调度的过程&#xff1a; scheduler是集群的调度器&#xff0c;主要任务就是把pod部署到节点上。 自动调度&#xff1a; 1、公平&#xff0c;保证每个可用的节点都可以部署pod 2、资源的高效利用&#xff0c;集群当中…

C/C++内存详解

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 C/C内存模型C语言动态内存管理mallocrealloccallocfree C动态内存申请new 操作符delete 操作符注意事项用法示例 operator new和operator delete函数内存泄露 C/C内存模型 让我们先来看看这段代码&a…