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

C++ 模板

时间:2024-06-07 15:28:48浏览次数:17  
标签:const 函数 C++ template Data 模板 特化

一.非类型模板参数

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

类型形参:类作为模板参数,typename/class T (T就是类型形参)

非类型形参:内置类型作为模板参数,int double char ...(在C++20前只有 int 可以传)

这样我们就可以随便定义栈的大小。

注:因为n是常量所以是不能修改的。

(补充:array容器)

用array创建的数组和普通定义的数组有什么优势呢?

我们知道普通数组的越界检查是不完全的,越界读取数据是不报错的。

而array创建的对象数组越界检查更严格。

但array对比vector,array是直接在栈上建数组,而vector是在堆上建。(栈的空间比堆小,需要存入大量数据建议用vector)

typename标识模板中的类型

这里为什么需要加上typename?

为了帮助编译器知道iterator是一个类型而不是变量或者其他东西。

vetcor<T> 因为类模板没有经历实例化,vector类里面没有进行检查可能会有错误,所以编译器不会进入类里面去找iterator,就不能确定iterator是一个类型还是静态成员变量等等。

二.模板的特化

1.函数模板特化

函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

template<class T>
bool Less(T left, T right)
{
	return left < right;
}
//函数模板特化(对Date*类型特殊处理)
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return left < right;
}

函数模板很容易出错,看下面代码为什么出错呢?

因为模板const修饰的是传过来的形参,

所以特化的模板const也应该修饰形参,而特化模板形参是指针,const Date*& left修饰的是指针指向的内容。Date* const& left 才是修饰的形参指针本身。

所以不建议用函数模板特化,需要特殊处理建议直接写函数重载(与模板实例化的函数)

2.类模板特化

全特化


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

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
//对int,char类型特殊处理
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;
}
int main()
{
	TestVector();
}

偏特化

偏特化分为两种

1.部分特化

2.对参数进行限制 (eg.必须为指针类型,&引用类型)

1.部分特化

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
// 将第二个参数特化为int
//只要第二个参数为int 就走这个偏特化<int,int>也会走
template <class T1>
class Data<T1, int>
{
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

2.对参数进行限制

template<class T1, class T2>
class Data
{
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:T1 _d1;
	   T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		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); 
    // 调用特化的指针版本
}

如果为指针类型,就会进入指针偏特化模板。

那么模板中的T1,T2是就是指针呢?

可以看到,T1double T2int ,如果为指针类型应该都为8.

三.模板分离编译

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

// 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;
}

编译运行后为什么找不到我们写的函数模板Add呢?

简单来说,函数模板没有实例化,没有函数地址,没有存入符号表,所以链接不到。

在test.cpp中普通函数会被编译生成函数地址存入符号表,而模板函数不知道实例化成什么,就不会被编译生成地址。

main.cpp中调用Add函数虽然知道实例化成什么,但只有声明没有定义。

那么怎么解决呢?
最好的方法就是声明和定义放在同一个文件上。

还有就是在定义文件中显示实例化。(不建议用因为要事先自己写入实例化的类型,太麻烦)

函数声明定义都放在同一个文件,还需要写声明吗?
虽然声明不写也可以,但在类模板中,短的函数直接写定义,默认inline。长的函数建议声明写在类里面,定义在类外(仍在同一个文件)。

模板:
优点:
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
缺陷:
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

标签:const,函数,C++,template,Data,模板,特化
From: https://blog.csdn.net/wws7920/article/details/139480249

相关文章

  • 免费,C++蓝桥杯比赛历年真题--第14届蓝桥杯省赛真题(含答案解析和代码)
    C++蓝桥杯比赛历年真题–第14届蓝桥杯省赛真题一、选择题答案:A解析:C++中bool类型与char类型一样,都需要1byte。一些其他类型的占用字节数:short:2byte,int:4byte,longlong:8byte,double:8byte,故答案为A。答案:C解析:A中结构体中可以定义成员变量,也可以定义只有该结......
  • 10_1、C++继承与派生:声明与继承关系
    声明与继承关系继承派生概念派生类声明派生类从基类继承的过程吸收基类成员修改基类成员添加新成员继承关系公有继承保护继承私有继承继承派生概念类的继承就是新类由已经存在的类获得已有特性。类的派生则是由已经存在的类产生新类的过程。由已有类产生新类时,新......
  • 自媒体必用的50 个最佳 ChatGPT 社交媒体帖子提示prompt通用模板教程
    在这个信息爆炸的时代,社交媒体已经成为我们生活中不可或缺的一部分。无论是品牌宣传、个人展示,还是日常交流,我们都离不开它。然而,要在众多信息中脱颖而出,吸引大家的关注并不容易。这时候,ChatGPT这样的AI写作工具就显得特别有用了。ChatGPT不仅能帮你快速生成高质量的内容,还能给你......
  • 【NOI】C++程序结构入门之循环结构二-for循环
    文章目录前言一、for循环1.导入2.语法3.使用场景4.条件控制5.小结二、例题讲解问题:1264-4位反序数问题:1085-寻找雷劈数问题:1057-能被5整除且至少有一位数字是5的所有整数的个数问题:1392-回文偶数?问题:1090-同因查找问题:1446.人口增长问题三、总结四、感谢......
  • C++ -- 引用
    什么是引用?引用其实就是一个变量的别名。也就是说,你可以通过引用的名称去访问原来的那个变量。其操作符很简单,就是在变量前面加上&。一个很简单的例子://variableinti;//referencevariablesint&r=i;i=5;cout<<"valueofiis:"<<i<<endl;......
  • C++11原子操作
    目录1.什么是原子操作2.为什么需要原子操作?3.C++中的原子操作4.原子操作使用及注意5.应用场景6.使用原子操作的最佳实践7.原子操作与锁机制的比较8.总结1.什么是原子操作        原子操作是一种不可分割的操作,即在多线程环境中,这些操作要么全部执行完成,要么......
  • C++中的priority_queue和deque以及适配器
    C++中的priority_queue和deque一丶priority_queue1.1priority_queue的介绍1.2priority_queue的使用1.3priority_queue的模拟实现二丶deque2.1deque的简单介绍2.2deque的缺陷2.3为什么要选择deque作为stack和queue的迭代器三丶容器适配器3.1什么是适配器3.2S......
  • C++ Template
    一、Template什么是template?重要性如何?下面我就说道说道:无性生殖不只是存在于遗传工程中,对于程序员而言,它也是一个由来已久的动作。过去,我们只不过是以一个简单而基本的工具,也就是一个文字编辑器,重复的复制代码。今天,C++提供给我们一个更好的繁殖方法:template。复......
  • 【C++进阶】深入STL之list:高效双向链表的使用技巧
    ......
  • C++Primer Plus第12章类和动态内存分配--再谈定位new运算符----12.8
    12.5.3再谈定位new运算符本书前面介绍过,定位new运算符让您能够在分配内存时能够指定内存位置。第9章从内置类型的角度讨论了定位new运算符,将这种运算符用于对象时情况有些不同,程序清单12.8使用了定位new运算符和常规new运算符给对象分配内存,其中定义的类的构造函数......