C++11 模板机制:
① 函数模板
② 类模板
模板的使用:
① 范围:模板的声明或定义只能在全局或类范围进行,不可以在局部范围(如函数)
② 目的:为了能够编写与类型无关的代码
函数模板:
- 格式:
template <类型形参表或成模板参数列表> //类型函数声明
返回类型 函数名 (形参表) {
函数体
}
- 注意:
模板参数表:定义在类或函数定义中用到的类型或值
类型形参表:可包含基本数据类型和类类型。类型形参需要加class/typename。
其中class和typename等价
T-通用的数据类型,名称可替换,通常为大写
类模板:
- 格式:
template <类型形参表> //类型参数声明
class 类名 {
类模板的代码
}
template <类型参数表> //定义在类模板之外的函数必须以关键字template开始
返回类型 类名 类型名表::成员函数n(形参表)
{
成员函数定义体
}
一、模板的优化
1.模板的右尖括号
/*
1.模板的右尖括号
*/
#include <iostream>
#include <vector>
using namespace std;
// 类模板Base
template<typename T>
class Base {
public:
// 遍历容器的操作函数traversal()
void travelsal(T& t) {
auto it = t.begin();
for(;it!=t.end();++it) {
cout<<*it<<" ";
}
cout << endl;
}
};
int main() {
vector<int> v{1,2,3,4,5,6,7,8,9};
Base<vector<int>> b;
b.travelsal(v);
return 0;
}
① c++98标准下的编译
g++ -std=c++98 4.模板的优化.cpp -o app
② c++11标准下的编译
g++ -std=c++11 4.模板的优化.cpp -o app
2.默认模板参数
在C++98/03标准中
① 类模板可以有默认的模板参数
② 不支持函数的默认模板参数
① 在C++98/03标准中,类模板可以有默认的模板参数
#include <iostream>
#include <vector>
using namespace std;
// 2.默认模板参数
/*
在C++98/03标准中
① 类模板可以有默认的模板参数
② 不支持函数的默认模板参数
*/
// ① 类模板可以有默认的模板参数
template<typename T=char,T t = 'a'>
class Test{
public:
void printString() {
cout<< "current value: " << t <<endl;
}
};
int main() {
Test<> t;
t.printString();
Test<char,'b'> t1;
t1.printString();
return 0;
}
总结: 对于类模板,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化
② 在C++98/03标准中,不支持函数的默认模板参数
#include <iostream>
#include <vector>
using namespace std;
// 2.默认模板参数
/*
在C++98/03标准中
① 类模板可以有默认的模板参数
② 不支持函数的默认模板参数
*/
// ② 不支持函数的默认模板参数
template<typename T=int> // C++98/03不支持这种写法, C++11中支持这种写法
void print(T t) {
cout<< "current value: " << t <<endl;
}
int main() {
print(1314);
return 0;
}
c++11支持对函数模板的默认参数
总结:当所有模板参数都有默认模板,函数模板的调用如同一个普通函数。
③ C++11 结合默认模板参数和模板参数自动推导
#include <iostream>
#include <string>
using namespace std;
template<typename R=int,typename N>
R test(N arg) {
return arg;
}
int main() {
auto ret1 = test(666);
cout<<"return value-1: "<<ret1<<endl;//666
auto ret2 = test<double>(54.321);
cout<<"return value-2: "<<ret2<<endl;//54.321
auto ret3 = test<int>(54.321);
cout<<"return value-3: "<<ret3<<endl;//54
auto ret4 = test<char,int>(100);
cout<<"return value-4: "<<ret4<<endl;//d
return 0;
}
④ C++11 同时使用默认模板参数和模板参数自动推导
#include <iostream>
#include <string>
using namespace std;
// 函数的定义
template<typename T,typename U = char>
void func(T arg1 = 99,U arg2 = 99) {
cout<<"arg1: "<<arg1<<", arg2: "<<arg2<<endl;
}
int main() {
// 模板函数调用
func('a');
func(97,'a');
// func();//编译报错
return 0;
}
func();编译出错是必须确定出T的数据类型,才能使用。
总结:
① 若可以推导出参数类型,就使用推导出的类型
② 若函数模板无法推导出参数类型,则编译器会使用默认模板参数
③ 若无法推导出模板参数 且 没有设置默认模板参数,编译器会报错
概括:
① 模板参数类型的自动推导是根据模板函数调用时指定的实参进行推导的,无实参则无法推导
② 模板参数类型的自动推导不会参考函数模板中指定的默认参数
⑥ 函数模板使用时,自动类型推导,必须推导出一直的数据类型T,才可以使用
#include<iostream>
using namespace std;
template<class T>
T add(T a,T b) {
return a + b;
}
// 1.自动类型推导,必须推导出一致的数据类型T,才可以使用
int main() {
int a=1;
int b=2;
char c='c';
add(a,b);//正确,可以推导出一致T
add(a,c);//错误,推导不出一致的T
}
3.类模板
类模板:
类模板允许用户为类定义一种模式,使得类中的某些数据成员、成员函数的参数或成员函数
的返回值能取任意类型。类模板的成员函数被认为是函数模板。
template <类型形参表> //类型参数声明
class 类名 {
类模板的代码
}
template<类型形参表> //定义在类模板之外的函数必须以关键字template开始
返回类型 类名 类型名表::成员函数n(形参表) {
成员函数定义体
}
① 实例化类模板
1.实例化类模板
① 类模板不能直接使用,必须先实例化为相应的模板类。
② 定义类模板之后,创建模板类:
类模板名<类型实参表> 对象表;
类型实参表与类模板中的类型形参表相匹配
#include <iostream>
using namespace std;
/*
实例化类模板
① 类模板不能直接使用,必须先实例化为相应的模板类。
② 定义类模板之后,创建模板类:
类模板名<类型实参表> 对象表;
类型实参表与类模板中的类型形参表相匹配
*/
template<class T1,class T2>
class Student{
public:
T1 name;//名字
T2 age;//年龄
Student(T1 k,T2 v) : name(k),age(v) {};
// 重载操作符:operator ,对小于号< 进行重载
bool operator < (const Student<T1,T2>& p) const;
};
template<class T1,class T2>
bool Student<T1,T2>::operator < (const Student<T1,T2>& p) const {
// Pair的成员函数 operator< "小"的意思是关键字小
return age < p.age;
}
int main() {
Student<string,int> stu1("Sakura",8);//实例化出一个类Pair<string,int>
cout <<"My name is " << stu1.name << ",and age is " << stu1.age <<endl;
Student<string,int> stu2("Luffy",14);//实例化出一个类Pair<string,int>
cout <<"My name is " << stu2.name << ",and age is " << stu2.age <<endl;
bool isYoung = stu1.operator<(stu2);
if(isYoung) {
cout<<stu1.name<<" is younger than him!!!\n";
}else {
cout<<stu2.name<<" is younger than her!!!\n";
}
return 0;
}
// 2.类模板实例
#include <iostream>
#include <string>
#include <initializer_list>
using namespace std;
template <typename T>
class Array { //类模板
int size;
T* p;
public:
Array();//默认构造函数
Array(int n);//重载构造函数
Array(initializer_list<T> lst);
T& operator[](int) ;//[]重载函数
int getSize();//获取数组大小
};
// 默认构造函数
template <typename T>
Array<T>::Array() {
size = 10;
p = new T[size];
}
// 重载构造函数
template<typename T>
Array<T>::Array(int n) {
size = n;
p = new T[size];
}
// 下标运算符重载函数
template<typename T>
T& Array<T>::operator[](int i) {
if(i>=0 && i<size) {
return p[i];
}
}
template<typename T>
Array<T>::Array(initializer_list<T> lst){
size = lst.end() - lst.begin();
p = new T[size];
//cout<<"size大小:"<<size<<endl;
int k=0;
for (auto it = lst.begin(); it != lst.end(); ++it)
{
//cout<<*it<<endl;
p[k++] = *it;
}
}
template<typename T>
int Array<T>::getSize(){
cout<<"获取size:"<<size<<endl;
return size;
}
int main() {
Array<int> arr1(2);
arr1[0] = 520;
arr1[1] = 1314;
Array<int> arr2({1,2,3,5});
cout<<"==============打印arr1数组=============="<<endl;
int size = arr1.getSize();
for (int i = 0; i < size; i++) {
cout<<arr1[i]<<" ";
}
cout<<endl;
cout<<"==============打印arr2数组=============="<<endl;
size = arr2.getSize();
for (int i = 0; i < size; i++) {
cout<<arr2[i]<<" ";
}
cout<<endl;
int a=10;int b=20;int c=30;
cout<<"==============打印arr3数组=============="<<endl;
Array<int> arr3({a,b,c});
size = arr3.getSize();
for (int i = 0; i < size; i++) {
cout<<arr3[i]<<" ";
}
cout<<endl;
return 0;
}
学习initializer_list,可以看这篇博客
C++ 函数传递多参数处理 可变参数模板_c++ 可变参数传递_Mr.禾的博客-CSDN博客https://blog.csdn.net/qq_24447809/article/details/114793958
#include <iostream>
#include <string.h>
#include <initializer_list>
using namespace std;
template <typename T>
class Array { //类模板
int size;
T* p;
public:
Array();//默认构造函数
Array(int n);//重载构造函数
T& operator[](int) ;//[]重载函数
int getSize();//获取数组大小
};
// 默认构造函数
template <typename T>
Array<T>::Array() {
size = 10;
p = new T[size];
}
// 重载构造函数
template<typename T>
Array<T>::Array(int n) {
size = n;
p = new T[size];
}
// 下标运算符重载函数
template<typename T>
T& Array<T>::operator[](int i) {
if(i>=0 && i<size) {
return p[i];
}
}
template<typename T>
int Array<T>::getSize(){
cout<<"获取size:"<<size<<endl;
return size;
}
class Student {
public:
int no;
char name[10];
Student() {}
Student(int n,const char* s) {
no = n;
strcpy(name,s);//使用strcpy为字符串数组赋值
}
Student &operator=(Student& s) {//赋值重载构造函数
no = s.no;
strcpy(name,s.name);
return *this;
}
void display() {
cout<<"学号: " << no <<",姓名: "<<name<<endl;
}
};
int main() {
cout << "学生列表" << endl;
Array<Student> arr(4);
int size = arr.getSize();
Student x(1, "Tom"), y(2, "Jerry"), z(3, "Sakura");
Student other = x;
arr[0] = x; arr[1] = y; arr[2] = z,arr[3] = other;
for (int i=0;i < size;i++) {
arr[i].display();
}
return 0;
}
② 类模板作为函数参数
#include <iostream>
using namespace std;
//类模板作为函数参数
template<class T>
class A {
T x;
public:
A(T a) {
x = a;
}
T abs() {
if(x<0) return -x;
else return x;
}
};
//函数模板中模板类作为形参
template<class T>
void func(A<T> x) {
cout<<x.abs()<<endl;
}
int main()
{
//建立对象
A<int> a(-5);
A<double> b(-5.8);
func(a);
func(b);
}
③ 函数模板作为类模板的成员
类模板中的成员函数可以是一个函数模板
// 函数模板作为类模板的成员
#include<iostream>
using namespace std;
template<class T>
class A{
public:
template<class T2>
void print(T2 t) {cout<<t;}//成员函数模板
};
int main() {
A<int> a;
//成员函数模板func被实例化
a.print('K'); //K
cout<<endl;
a.print("hello"); //hello
cout<<endl;
a.print(123); //123
cout<<endl;
return 0;
}
④ 类模板中也可以使用非类型参数,即值参数
#include<iostream>
using namespace std;
template<class T,int size>
class A{
public:
T sum(T a) {
return a+size;
}
};
int main(){
A<char,3> a;
cout<<a.sum('a')<<endl;// d
return 0;
}
⑤ 类模板的友元函数
template<class T>
class A{
public:
T value;
A() {}
A(T v):value(v){}
friend void func1();//与参数类型无关的友元函数
friend void func2(A<T>& a);//与参数类型有关的友元函数
};
template<class T>
void func1(){
cout<<"func1"<<endl;
}
template<class T>
void func2(A<T>& a){
cout<<"func2 value:" <<a.value<<endl;
}
int main() {
A<double> a(1.2);
func1<int>();//func1是模板类A<int>的友元函数
func1<double>();//func1是模板类A<double>的友元函数
func2<double>(a);//func2是模板类A<double>的友元函数
}
4.可变参数模板
① 可变参数模板例子
可变参数模板:
① C++11增强了模板功能,允许模板定义中包含0到任意个模板参数
② 对参数进行了高度泛化,能表示任意个数,任意类型的参数
③ 是一个接收可变数目参数的模板函数或模板类
④ 可变数目的参数被称为参数包
语法:
template<class... T>
void f(T... args);
声明一个参数包T... args,这个参数包中可以包含0到任意个模板参数
/*
可变参数模板:
① C++11增强了模板功能,允许模板定义中包含0到任意个模板参数
② 对参数进行了高度泛化,能表示任意个数,任意类型的参数
③ 是一个接收可变数目参数的模板函数或模板类
④ 可变数目的参数被称为参数包
语法:
template<class... T>
void f(T... args);
声明一个参数包T... args,这个参数包中可以包含0到任意个模板参数
*/
#include <iostream>
#include <map>
using namespace std;
template<typename T,typename... Args>//func是可变参数函数模板 有一个名为T的类型参数和一个名为args的模板参数包
void func(const T&t,const Args&...rest){//func函数列表包含一个const& 类型的参数 指向T的类型,名为rest的函数参数包
cout<<sizeof...(rest)<<endl;//输出函数参数的数目
};
int main(){
int a=0;double d=3.14;string str="heheda";
func(a,str,42,d);//包中有三个参数
func(str, 42, d);//包中有两个参数
func(d, str);//包中有一个参数
func("hi");//空包
return 0;
}
② 可变参数模板的展开
可变参数模板的展开:通过展开参数包的方式来获取参数包中的每个参数
两种展开方式:
① 通过递归函数来展开参数包
② 通过逗号表达式来展开参数包
/*
可变参数模板的展开:通过展开参数包的方式来获取参数包中的每个参数
两种展开方式:
① 通过递归函数来展开参数包
② 通过逗号表达式来展开参数包
*/
#include <iostream>
using namespace std;
//递归终止函数
void print()
{
cout << "empty!!!" << endl;
}
//展开函数
template<class T,class... Args>
void print(T head,Args... rest) {
cout<<"param:"<<head<<endl;
print(rest...);
}
int main(void)
{
print(1,2,3,4);
return 0;
}
③ 可变模板参数求和
#include <iostream>
using namespace std;
template<typename T>
T sum(T t){
return t;
}
template<typename T, typename ... Types>
T sum (T first, Types ... rest) {
return first + sum<T>(rest...);
}
int main(void)
{
cout<<sum(1,2,3,4)<<endl; //10
return 0;
}
④ 逗号表达式展开参数包
#include <iostream>
using namespace std;
//逗号表达式展开参数包
template <class T>
void printArg(T t)
{
cout << t << " ";
}
template <class ...Args>
void expand(Args... args)
{
int arr[] = {(printArg(args), 0)...};
}
int main(){
expand(1,2,3,4);
cout<<endl;
return 0;
}
5.可变参数模板类的展开
//前向声明
template<typename... Args> struct Sum;
//基本定义
template<typename First, typename... Rest>
struct Sum<First, Rest...>
{
enum { value = Sum<First>::value + Sum<Rest...>::value };
};
//递归终止
template<typename Last>
struct Sum<Last>
{
enum { value = sizeof (Last) };
};
学习和参考一下文章:
模板的优化 | 爱编程的大丙 (subingwen.cn)https://subingwen.cn/cpp/template/C++11 模板用法总结_herryone123的博客-CSDN博客https://blog.csdn.net/kenjianqi1647/article/details/118207899