C++模板进阶从非类型参数到特化全解,避开90%人踩过的坑,面试再也不怕模板题!
·
模板进阶
文章目录
1.非类型模板参数
类型模板参数:
- template里面是class/typename这种
template <class T, class Container = std::vector<T>, class Compare = Less<T> > class priority_queue { 。。。。。 }
举例
- 只传参数不赋值
template<size_t N = 10, bool flag = false>
class Stack
{
private:
int _a[N];
int _top;
};
int main()
{
Stack<> s0;
Stack<5> s1;
Stack<10> s2;
return 0;
}
注意:
- 只用于整形家族:
char ,int,long,‼️bool‼这种(c++20后面支持了double) - 必须在编译期就能确定结果
- 给了非类型模板参数 缺省值,不能直接不加<>就创建——>必须要:
Stack<> s0;这样写(c++ 后面支持了)
array
其实用vector更好,不过array是栈上,vector是堆上
- c++的代替C语言数组的一个数据结构
- 用法:
array<int, 100> a1array<double, 10> a2
- 好处:
- 越界检查更方便
2.函数模板的特化
不建议用
2.1引出:
下面的代码没有对指针的比较问题
// 函数模板 -- 参数匹配
template<class T>
bool LessFunc(T left, T right)
{
return left < right;
}
int main()
{
cout << LessFunc(1, 2) << endl; // 可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << LessFunc(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << LessFunc(p1, p2) << endl; // 可以比较,结果错误❌
return 0;
}
特化示例:
template<class T>
bool LessFunc(T left, T right)
{
return left < right;
}
template<>
bool LessFunc<Date*>(Date* left, Date* right)
{
return *left < *right;
}
更推荐将特化改成函数:
当然特化和函数也可以同时存在,不过还是会调用函数,因为特化还是模板,使用时会实例化,麻烦
template<class T>
bool LessFunc(T left, T right)
{
return left < right;
}
bool LessFunc (Date* left, Date* right)
{
return *left < *right;
}
2.2格式
template<>
bool LessFunc<Date*>(Date* left, Date* right)
{
return *left < *right;
}
- 必须要先有一个 函数模板
- 然后创造一个相似的函数模板,区别如下:
- 空的<> : 关键字 template 后面 尖括号 <>,是空的
- **再加<>:**函数名后跟一对尖括号,尖括号中指定需要特化的类型
- **形参相同:**必须要和模板函数的基础参数类型完全相同
2.3特化的问题
一步一步说:
引出:前面的模板写的不是很好,为防止拷贝构造,应该把T都改成const T &——>
template<class T>
//bool LessFunc(T const & left, T const & right)
bool LessFunc(const T& left, const T& right)
{
return left < right;
}
但是,特化就对不上了——>改一下特化为:
bool LessFunc (const Date*& left, const Date*& right)
{
return *left < *right;
}
‼️这时候是错的
原因:
注意看
const Date*& left,const是在*前面——>left指向的内容不能改变,而left可变——>和模板中的:
const T& left对不上了
——>应该写成:
bool LessFunc ( Date* const & left, Date* const & right)
{
return *left < *right;
}
所以,更推荐写成“和普通函数的重载”
3.类模板的特化
相当于是模板的重载——>就像函数重载一样,类型不同,调用的模板/函数 虽然重名,但不是一个
格式
特化类模板的书写:
- 必须要先有一个 类模板
- 然后创造一个相似的函数模板,区别如下:
- template后的<>中,内容不确定‼️
- **class 类名后加<>:**函数名后跟一对尖括号,尖括号中指定需要特化的类型
特化类模板的实例化:
- 类模板名+尖括号(其中有对应的类型)+类名
- 例如:
Data<int&, double&> d9;
- 例如:
种类
无特化:
template<class T1, class T2>
class Data
{
};
全特化:
- 如果实例化时的两个类型和如下特化模板相同,就调用对应的全特化模板
template<>
class Data<int, char>
{
};
偏特化/半特化:
- 如果实例化时,对应位置的类型符合,就调用对应的偏特化模板
template<class T1>
class Data<T1, double>
{
};
传指针的特殊偏特化:
- 实例化时<>中是指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
};
传引用的特殊偏特化:
- 实例化时<>中是引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
};
注意点:
实例化时,<>中的内容,如果传的是*(指针)或&(引用),在偏特化类模板中对应的内容是原类型‼️
⬇️
对于他俩(以及他俩的混合——>第三列)
| 传指针的特殊偏特化: | 传引用的特殊偏特化: | 既有传指针又有传引用 | |
|---|---|---|---|
| 偏特化的写法 | template <typename T1, typename T2> class Data <T1*, T2*> { }; | template <typename T1, typename T2> class Data <T1&, T2&> { }; | template <typename T1, typename T2> class Data <T1&, T2*> { }; |
| 实例化 | Data<int*, char*> d7; | Data<int&, double&> d9; | Data<int&, int*> d10; |
| T1和T2的实际指代 | T1是int T2是char | T1是int T2是double | T1是int T2是int |
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
// 全特化
template<>
class Data<int, char>
{
public:
Data() { cout << "Data<int, char>" << endl; }
};
// 偏特化/半特化
template<class T1>
class Data<T1, double>
{
public:
Data() { cout << "Data<T1, double>" << endl; }
};
template<class T1>
class Data<T1, char>
{
public:
Data() { cout << "Data<T1, char>" << endl; }
};
// 偏特化,传的类型是指针
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data()
{
cout << "Data<T1*, T2*>" << endl;
}
};
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data()
{
cout << "Data<T1&, T2&>" << endl;
}
};
template <typename T1, typename T2>
class Data <T1&, T2*>
{
};
4.模板声明定义分离问题
为什么模版不建议?
➡️有关 编译链接+预处理 的知识,可以点击这里进行学习⬅️
答:先假设有3个文件:func.h,func.cpp,main.cpp
- 声明和定义分离(在func.h中声明,func.cpp定义,main.cpp中使用)的情况:
- 虽然有声明和定义,但是func.cpp中没有使用➡️没有实例化➡️没有函数生成‼️
- main.cpp编译的时候,即使把.h复制进来,也无法找到函数❌
- 声明、定义部分离(都在func.h中)
- main.cpp中使用的时候有定义➡️实例化出函数✅
显示实例化
用来实现模板的声明定义分离
写法:
template
int add<int>(int, int);
- 直接一个template,后面什么也不加
- 下面写函数,函数名后面写<>,尖括号里面写类型
**原因:**写这一行强制生成 int 版本,链接就能找到。
使用频率:日常几乎不用,只用来强行实现分离编译。
之前在模板初阶的时候也讲了一个显示实例化,和这个不是一回事,
原来的显示实例化:
- 调用侧手动告诉编译器 T 是什么。
- 当编译器无法推导出模板参数类型时必须用:
Add<int>(a1, d1); // 强制T为int Add<double>(a1, d1); // 强制T为doubletemplate<class T> T* func1(int n) { return new T[n]; } int main() { double* p1 = func1<double>(10);//当编译器无法推导出模板参数类型时必须用显式实例化 return 0; }

更多推荐


所有评论(0)