模板高级使用(非类型模板参数,特化,分离编译)

在这里插入图片描述

文章目录

    • 模板没有实例化取内嵌类型报错问题
    • 非类型模板参数
    • 模板的特化
      • 函数模板的特化
      • 类模板的特化
        • 1.全特化
        • 2.偏特化
    • 模板的分离编译

模板没有实例化取内嵌类型报错问题

首先在这里分享一个模板的常见报错问题。就是模板的在没有实例化的情况下去取模板类里面的内嵌类型这时候的报错问题。

报错信息:
error C2760: 语法错误: 意外标记 “标识符”,应为 “;”
error C7510: “const_iterator”: 类型 从属名称的使用必须以“typename”为前缀
image.png

错误就在框线里面的哪一行代码,这段代码是使用模板编写的可以打印任意容器内的数据的函数。
报错的原因是在编译的时候,Con作为一个模板参数这时候还没有实例化出具体类型,编译器并不认识Con以及后面这一块是什么东西,因为类型没有实例化也就不能去取Con的内嵌类型。所以这时候就报错了。加上typename就是告诉编译器,Con是一个模板类型,等Con实例化之后再去取Con的内嵌类型。相当于是一个声明。

这里可以类比函数声明,如果调用了一个函数没有声明也没有定义,那么这时候是编译不通过的,报错就是找不到”xxx“标识符,这个错误是在编译的时候进行语法检查的时候检查出来的。如果加上这个函数的声明,那么编译时不会报错的。因为有了声明就告诉了编译器这个函数存在,你等着链接的时候再去找,如果在链接的时候还是找不到就会报错链接错误。
这里以add函数为例:image.png
现在加上函数声明,但是不写函数定义。
image.png
这时候报的就是LNK也即是链接错误。

非类型模板参数

以前使用的模板都是将类型定义成模板参数,比如内置类型int等以及自定义类型等等。但是其实常量也是可以作为模板参数的。

#include<iostream>using namespace std;template<class T,size_t N>
class Time
{
public:private:T a[N];
};int main()
{Time<int,100> t1;return 0;
}

如上这里的第二个模板参数就是一个常量,但是非类型模板参数只能是int类型的常量,其他比如:double,字符串或者自定义类型变量那么都是不行的。
在类内部这个非类型模板参数也是当作常量使用的,因为定义数组的大小只能使用常量(不考虑C99中的变长数组)
其次模板必须在编译的时候就能确定模板参数的类型,这也决定了模板不能够分离编译,一旦分离就会报错。

模板的特化

使用模板可以让编译器替我们完成一些重复的工作,同时也可忽略类型,让我们只关注代码逻辑的本身,但是有些特殊情况模板的默认处理逻辑并不能完成我们的需要,这时候就有了模板的特化。

函数模板的特化

比如模板定义了一个比较函数

template<class T> 
void compare(const T& x, const T& y)
{return x > y;
}

可以给这个函数传int,double等等,编译器都会自动生成一份与其匹配的代码,但是当想要比较字符串的时候。比如下面:

template<class T> 
bool compare(const T& x, const T& y)
{return x > y;
}int main()
{//Time<int,100> t1;char arr1[] = "hello kisskernel";char arr2[] = "hello kisskernel";cout << compare(arr1, arr2) << endl;const char* p1 = "hello kisskernel";const char* p2 = "hello kisskernel";cout << compare(p1, p2) << endl;return 0;
}

这时使用模板生成的比较函数,比较的是地址,而不是字符串,所以就需要模板的特化来解决这种特殊情况。
这里有两种解决方案:
1.直接写一个重载函数,对于特殊情况完成特殊处理(推荐使用)。
2.将函数模板根据特殊情况进行特化,也叫做专用化,实际也是自己写一个函数。
并且比写一个重载函数还要复杂,所以一般推荐使用第一种方式解决。

1.重载

bool compare(char* x,char* y)
{return strcmp(x, y) == 0;
}
bool compare(const char* x, const char* y)
{return strcmp(x, y) == 0;
}

针对上面的arr1/arr2类型char重载一个函数,针对const char又重载了一个函数。

2.模板特化

//针对char*类型的参数
template<>
int compare<char*>(char* const& left, char* const& right)
{cout << "compare<char*>" << endl;return strcmp(left, right);
}
//针对const char* 的两种特化方式
template<>
int compare<const char*>(const char* const& left, const char* const& right)
{cout << "compare<const char*>" << endl;return strcmp(left, right);
}
//template < >
//int compare(const char* const& left, const char* const& right)
//{
//	cout << "in special template< >..." << std::endl;
//
//	return strcmp(left, right);
//}

因为C++的函数模板的特化规则过于诡异,很容易就会出现奇怪的报错。
比如下面这句报错:
error C2912: 显式专用化;“int compare<const char*>(const char ,const char )”不是函数模板的专用化
这种错误,不同的时将括号内的参数类型换一下,这些都是因为特化的函数模板没有和基础模板的参数完全相同。
所以这里详细讲解一下特化方式。
函数模板步骤
1.首先必须要有一个基础模板,也即是通用的模板
2.特化的时候template后面必须有尖括号<>
3. 函数形参表: 必须要和模板函数的基础参数类型完全相同
**注意:如果基础的模板参数带有const,比如上面代码通用模板的类型时const T&,我们特化出来的类型目的是将T替换,但是这时候特化后的T应该写在cosnt &的前面不然就会报错。因为如果T指针类型如int,那么const修饰的意义就变了,如果不改变T位置就是:const int& left,const从修饰&变成了修饰指针。之前是a指向的对象不能改变,现在变成int
不能改变了,所以要将T改变到const之前,不改变const的修饰意义。int
const &

如果基础模板的参数没有const那就无所谓了,但是如果参数时引用,那么特化后的参数也必须时引用。
下面代码简单演示:

template <class T>
int compare(T& left, T& right)
{std::cout << "in template<class T>..." << std::endl;return strcmp(left, right);
}template<>
int compare<char*>(char*& left, char*& right)
{return 0;
}
template<>
int compare<const char*>(const char*& left, const char*& right)
{return 0;
}

类模板的特化

类模板的特化分为全特化和偏特化。

1.全特化

顾名思义就是将模板参数全部确定化。


template<class T1,class T2>
class Date
{
public:Date(){cout << "template<class T1,class T2>" << endl;}
private:int _year;int _month;int _day;
};template<>
class Date<int,char>
{
public:Date(){cout << "Date<int,char>" << endl;}
private:int _year;int _month;int _day;
};

模板全特化这里就时根据特殊情况来实例化一个类完成特殊化处理。

2.偏特化

偏特化有的书上也叫做半特化,但是很少这样叫。
偏特化就是针对模板参数进行一些限制处理。

template<class T1, class T2,class T3>
class Date
{
public:Date(){cout << "template<class T1,class T2>" << endl;}
private:int _year;int _month;int _day;
};
template<class T1,class T2>
class Date<T1, T2, char>
{
public:Date(){cout << "Date<int,char>" << endl;}
private:int _year;int _month;int _day;
};template<class T1>
class Date<T1, char,int>
{
public:Date(){cout << "Date<int,char>" << endl;}
private:int _year;int _month;int _day;
};

比如这里三个模板参数,可以设置一个参数特化,或者时两个参数特化,但是必须是从右向左,且必须是连续的。这点类似于缺省参数。

偏特化除了限定参数为int,double等等之外还可以限定参数的类型,比如限定为T*或者是T&等。

template<class T1, class T2,class T3>
class Date
{
public:Date(){cout << "template<class T1,class T2>" << endl;}
private:int _year;int _month;int _day;
};template<class T1, class T2, class T3>
class Date<T1*,T2*,T3&>
{
public:Date(){cout << "template<class T1,class T2>" << endl;}
private:int _year;int _month;int _day;
};

比如这种就是模板限定了参数的类型。进行了更进一步的限制。

模板的分离编译

模板之前就说过是不支持分离编译的,会报出链接错误,分离编译的意思就是(声明在.h文件,定义的.cpp文件中)首先先讲为什么要进行分离编译呢?分离编译的优点是:便于维护并且可以增强工程代码的可读性。因为函数的声明,和类的框架都在头文件中。如果只需要了解一下大体框架只需要看头文件就可以了解了。细节内容就可以去cpp文件中查看。

模板为什么不支持分离编译

首先需要了解编译链接的过程,程序要经过预处理,编译,汇编,链接四个过程最后形成可执行文件。模板需要在编译之前推导出T的具体类型,然后实例化出具体的代码,然后再将实例化的代码编译为指令。

但是模板分离编译的时候cpp文件时分开编译的,在最后链接之前互相是独立的。所有此时模板不能推导出T的具体类型就不会实例化出具体代码。但是因为.h文件中包含函数的声明,所以main.cpp调用的地方可以走到链接,在链接的时候去找具体实现,此时因为找不到就会爆出链接错误。因此模板不支持分离编译。

解决方法:

  1. 将定义和实现都放在.h文件中。
  2. 模板显式实例化,在实现定义的cpp文件中显示指定T的类型。
template
class Test<int>template
void Func<int>(int a, int b)

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

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

相关文章

代码随想录|Day25|回溯05|491.非递减子序列、46.全排列、47.全排列II

491. 非递减子序列 本题并不能像 90.子集II 那样&#xff0c;使用排序进行树层去重。虽然题目没有明确不能排序&#xff0c;但如果排序了&#xff0c;集合本身就是递增子序列&#xff0c;这是LeetCode示例2中没有出现的。 所以本题的关键在于&#xff0c;如何在不排序的情况下对…

2024格行VS华为VS飞猫哪个是最值得购买随身WiFi?中兴随身WiFi好用吗?

经常出差旅行&#xff0c;或者户外工作的朋友因为长期在外&#xff0c;手机流量经常不够用&#xff0c;想必都是随身WiFi的忠实用户&#xff0c;但是也都被这款产品割韭菜割的头皮发麻。今天&#xff0c;我们统计了市面上最靠谱的、最热销、口碑最好的几款随身WiFi。排名依据来…

Java学习笔记(17)

集合进阶 单列集合 Collection List set Add clear remove contains isempty size Add方法可能也会添加失败 同理&#xff0c;可能删除失败 Contains细节 为什么要重写equals&#xff1f; 因为contains底层用的是object类中的equals方法&#xff0c;比较的是地址值&#xf…

为什么穷人什么都懂,就是不懂赚钱?2024金矿项目! 2024创业好项目 !2024创业新项目新商机! 2024超级机会

为什么穷人什么都懂&#xff0c;就是不懂赚钱&#xff1f;有位网友是这么说的&#xff0c;穷人的思维有一个致命的缺陷&#xff0c;就是追求确定性&#xff0c;进而失去了可能性。而赚钱的真相实际上非常残酷。世界上能够赚钱的事情必定是不确定的&#xff0c;能够赚取巨额财富…

万亿参数GPU!算力提升30倍!英伟达新核弹B200重磅发布!

关注文章底部的公众号,获取每日AI资讯 前沿 3月18日-21日期间,英伟达在美国圣何塞召开GTC大会。创始人黄仁勋也在GTC大会上,做了一场长达两小时的开幕演讲,展示了其在AI芯片、机器人、汽车等领域的最新研发成果和技术进展,号称让全世界用上AI。 全球头号人工智能领域开发…

算法第三十一天-直方图的水量

直方图的水量 题目要求 解题思路 使用面向列的计算比面向行的计算更加容易。我们只需要考虑当前的位置的左右最高模板的高度。 方法一、暴力解法 每个位置能接到多少雨水&#xff0c;很容易想到[木桶效应]&#xff0c;即是由两边最短的木板限制的。那么直观思路就是&#x…

扶贫惠农推介系统|基于jsp技术+ Mysql+Java+ B/S结构的扶贫惠农推介系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

Python学习:元组

Python 元组概念 Python 中的元组&#xff08;tuple&#xff09;是不可变的有序集合。它是一种数据类型&#xff0c;类似于列表&#xff08;list&#xff09;&#xff0c;但在创建后不能被修改。元组使用圆括号 () 来表示&#xff0c;其中的元素可以是任意类型&#xff0c;并且…

初识数据库原理:为什么需要数据库?

初识数据库原理&#xff1a;什么是数据库&#xff1f; Chapter1&#xff1a;什么是数据库&#xff1f; 笔记来源&#xff1a;《漫画数据库》–科学出版社 1.1 为什么需要数据库&#xff1f; 文件应用的管理方式&#xff0c;数据会出现重复。 若各个部门各自管理自己一方的数…

2024年【T电梯修理】考试内容及T电梯修理作业考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 T电梯修理考试内容根据新T电梯修理考试大纲要求&#xff0c;安全生产模拟考试一点通将T电梯修理模拟考试试题进行汇编&#xff0c;组成一套T电梯修理全真模拟考试试题&#xff0c;学员可通过T电梯修理作业考试题库全真…

Linux手动创建用户不使用useradd【七步走完成】

文章目录 第一步&#xff1a;修改 /etc/passwd 文件第二步&#xff1a;修改 /etc/shadow 文件第三步&#xff1a;修改 /etc/group 文件第四步&#xff1a;新建用户家目录第五步&#xff1a;复制/etc/skel目录下的环境变量配置文件到家目录下第六步&#xff1a;修改家目录的权限…

AI系统性学习—LangChain入门

文章目录 1、LangChain入门1.1 简介1.2 架构1.3 核心概念1.2 快速入门1.3 安装 2、LangChain Prompt Template2.1 什么是提示词模版2.1 创建一个提示词模版2.2 聊天消息提示词模版2.3 模版追加示例 3、语言模型3.1 LLM基础模型3.2 LangChain聊天模型3.3 自定义模型3.4 输出解析…

基于springboot的stone音乐播放器的设计与实现

摘 要 随着我国经济的高速发展与人们生活水平的日益提高&#xff0c;人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加快的当下&#xff0c;人们更趋向于足不出户解决生活上的问题&#xff0c;stone音乐播放器展现了其蓬勃生命力和广阔的前景。与此同时&#xff0c;…

数电复试速成,期末速成

【拯救者】数字电路与逻辑速成(期末考研复试专升本)均可用 1️⃣先讲每章对应的基础和题目 2️⃣接着会讲对应的题目巩固 &#x1f357;提供文档下载 这里讲的是【 &#x1f337;速成&#x1f337; 速成&#x1f337; 速成】版本&#xff0c;按课本章节来&#xff0c; 抽取重…

【Unity】捕捉PC桌面的插件

【背景】 之前介绍了如何用一款名为uWindowCapture的Unity免费插件在Unity的Canvas上展示PC桌面。经过一段时间的使用,本篇继续分享此插件的一些功能和限制。 在此感谢作者Hecomi。 【特征和限制】 一般局域网络环境只能最多达到15帧的帧率,所以别幻想用来窜流游戏或者看电…

这个简单的生活方式,为你带来满满的幸福感

在今天文章的开头&#xff0c;我想请你思考一个问题&#xff1a;影响幸福感的最大因素是什么&#xff1f; 不妨先想一想&#xff0c;再往下拉&#xff0c;继续阅读。 可能不少朋友的回答&#xff0c;会是财富、事业、理想、生活环境、社会地位…… 这些因素当然对幸福感都非常重…

解决GNURadio自定义C++ OOT块-导入块时报错问题

文章目录 前言一、问题描述二、解决方法1、安装依赖2、配置环境变量3、重新编译及安装三、结果1、添加结果2、运行结果前言 本文记录在 GNURadio 自定义 C++ OOT 块后导入块时报错 AttributeError: module myModule has no attribute multDivSelect。 一、问题描述 参考官方教…

国内智能驾驶芯片领先供应商地平线智能驾驶芯片介绍

地平线国内智能驾驶芯片领先供应商&#xff0c;由国际著名机器学习专家余凯博士于2015年7月创建&#xff1b;2017年12月&#xff0c;地平线即推出了首款 智能芯片征程1和旭日1&#xff1b;2019年8月&#xff0c;宣布量产中国首款车规级智能芯片征程2并于同年10月发 布新一代AIo…

SUS-Chat-34B笔记

名称SUS-Chat: Instruction tuning done right团队南方科技大学、IDEA研究院CCNL团队代码地址https://github.com/SUSTech-IDEA/SUS-Chat简介具有超强多轮对话能力&#xff0c;擅长模仿人类思考过程&#xff0c;在各大榜单上超越同量级的模型。 介绍 SUS-Chat-34B模型是南方科…

用户行为分析是什么?为什么我们需要 bitmap?

本文非常好&#xff1a;https://blog.bcmeng.com/post/doris-bitmap.html meta搜也非常好&#xff1a;https://metaso.cn/ 用户行为分析是什么&#xff1f;简单说&#xff0c;就是围绕全体用户&#xff0c;做各种分析。用户就是一个个的 id。id 在不同方面有各种行为记录&…