C++模板的笔记1
C++ 函数模板
函数模板的定义
函数模板是一种可以生成不同类型函数的函数声明。函数模板的参数类型不是固定的,而是在调用时由实参类型推导出来。
语法:
template <typename 参数列表>
函数返回值类型 函数名(参数列表) {
函数体
}
示例:
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
该模板定义了一个名为 swap
的函数,该函数可以交换任意类型的两个变量。
函数模板的调用和参数推导
调用函数模板时,编译器会根据实参类型推导出模板参数类型。
示例:
int x = 1, y = 2;
swap(x, y); // 模板参数推导为 int
double a = 3.14, b = 2.71;
swap(a, b); // 模板参数推导为 double
函数模板的实现原理
函数模板的实现原理是:编译器会根据模板参数类型生成具体的函数代码,然后将该代码插入到程序中。
示例:
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
// 编译器会生成以下两个函数:
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
void swap(double& a, double& b) {
double temp = a;
a = b;
b = temp;
}
函数模板的重载
函数模板可以重载,即可以定义多个具有相同名称的函数模板,但它们的模板参数列表不同。
示例:
template <typename T>
void swap(T& a, T& b) {
// ...
}
template <typename T, typename U>
void swap(T& a, U& b) {
// ...
}
第一个 swap
模板可以用于交换相同类型的两个变量,而第二个 swap
模板可以用于交换不同类型的两个变量。
函数模板的参数默认值
函数模板的参数可以设置默认值,如果在调用函数模板时没有指定参数类型,则使用默认值。
语法:
template <typename T = 默认值>
void 函数名(参数列表) {
// ...
}
示例:
template <typename T = int>
void swap(T& a, T& b) {
// ...
}
该模板中,T
的默认值为 int
。
如果调用 swap
函数时没有指定参数类型,则编译器会将 T
推导为 int
。
int x = 1, y = 2;
swap(x, y); // T 推导为 int
函数模板的泛化和特化
函数模板的泛化是指:函数模板可以用于处理不同类型的数据。
函数模板的特化是指:针对特定的类型,可以提供更具体的实现。
语法:
template <typename T>
void 函数名(参数列表) {
// 通用实现
}
template <>
void 函数名<特化类型>(参数列表) {
// 特化实现
}
示例:
template <typename T>
void print(T value) {
cout << value << endl;
}
// 对 int 类型进行特化
template <>
void print(int value) {
cout << "int: " << value << endl;
}
该模板中,print
函数针对 int
类型进行了特化,提供了更具体的实现。
调用 print
函数时,会根据实参类型选择不同的实现。
int x = 1;
print(x); // 输出 "int: 1"
double y = 3.14;
print(y); // 输出 "3.14"
C++ 类模板
类模板的定义
类模板是一种可以生成不同类型类的类声明。类模板中的成员函数和成员变量的类型不是固定的,而是在类实例化时由实参类型推导出来。
语法:
template <typename 参数列表>
class 类名 {
// 类成员声明
};
示例:
template <typename T>
class Point {
public:
T x;
T y;
Point(T x, T y) {
this->x = x;
this->y = y;
}
void print() {
cout << "(" << x << ", " << y << ")" << endl;
}
};
该模板定义了一个名为 Point
的类,该类可以表示任意类型的点。
类模板的调用和参数推导
在C++17以上的编译器中,调用类模板时,编译器会根据实参类型推导出模板参数类型,你如果编译器是其他C++17以下版本,必须写上参数。
示例:
Point<int> p1(1, 2); // 实例化 Point 类,模板参数推导为 int
p1.print(); // 输出 "(1, 2)"
Point<double> p2(3.14, 2.71); // 实例化 Point 类,模板参数推导为 double
p2.print(); // 输出 "(3.14, 2.71)"
类模板的编译规则
类模板的编译规则如下:
- 类模板本身不会被编译,只有在类实例化时才会被编译。
- 类模板中的成员函数和成员变量的类型必须是模板参数类型或其他依赖于模板参数类型的类型。
类模板的参数默认值
类模板的参数可以设置默认值,如果在实例化类模板时没有指定参数类型,则使用默认值。
语法:
template <typename T = 默认值>
class 类名 {
// 类成员声明
};
示例:
template <typename T = int>
class Point {
public:
T x;
T y;
Point(T x, T y) {
this->x = x;
this->y = y;
}
void print() {
cout << "(" << x << ", " << y << ")" << endl;
}
};
该模板中,T
的默认值为 int
。
在C++17以上的编译器中,如果在实例化 Point
类时没有指定参数类型,则编译器会将 T
推导为 int
。
Point p1(1, 2); // T 推导为 int
p1.print(); // 输出 "(1, 2)"
类模板的泛化与特化
类模板的泛化是指:类模板可以用于表示不同类型的數據。
类模板的特化是指:针对特定的类型,可以提供更具体的实现。
语法:
template <typename T>
class 类名 {
// 通用实现
};
template <>
class 类名<特化类型> {
// 特化实现
};
示例:
template <typename T>
class Number {
public:
T value;
Number(T value) {
this->value = value;
}
void print() {
std::cout << value << std::endl;
}
};
// 对 int 类型进行特化
template <>
class Number<int> {
public:
int value;
Number(int value) {
this->value = value;
}
void print() {
std::cout << "int: " << value << std::endl;
}
};
该模板中,Number
类针对 int
类型进行了特化,提供了更具体的实现。
调用 Number
类的 print
函数时,会根据实参类型选择不同的实现。
Number<int> n1(1);
n1.print(); // 输出 "int: 1"
Number<double> n2(3.14);
n2.print(); // 输出 "3.14"
类模板的成员函数模板
示例解析:
- 该代码定义了一个名为
Num
的类模板,该类模板可以表示任意类型的数字。 Num
类模板有两个成员函数模板:show1
和show2
和show3
。show1
函数模板可以接受任意类型的参数。show2
在外部实现的,函数模板可以接受任意类型的参数。show3
函数模板的参数类型必须与类模板的参数类型相同,并且特例化成int类型。
代码示例:
#include <iostream>
using namespace std;
template <typename T>
class Num{
public:
template <typename T2> //成员方法模板泛化,类内部实现
void show1(T2 data){
cout<<"show1"<<endl;
}
template <typename T3> //成员方法模板泛化,类外实现
void show2(T3 data);
void show3(int data);//成员方法特化,类外实现
};
template <typename T>
template <typename T3>
void Num<T>::show2(T3 data)
{cout<<"show2"<<endl;}
template <typename T>
void Num<T>::show3(int data)
{cout<<"show3"<<endl;}
int main()
{
Num<int> n;
n.show1(1);
n.show2<double>(2);
n.show3(0);
return 0;
}
知识点:
- 类模板可以拥有成员函数模板。
- 成员函数模板的参数可以与类模板的参数相同或不同。
- 成员函数模板的定义需要在类模板的声明之后。
- 成员函数模板的实现在类模板的外部需要加上,格式:
template <typename T>
template <typename T3>
返回类型 类名<T>::方法(模板参数类型 参数){}
- 类模板可以拥有特化版成员函数模板,格式:
template <typename T>
返回类型 类名<T>::方法(指定的参数类型 参数){}
- 类模板的成员函数不能声明为虚函数,C++ 标准规定:类模板的成员函数不能声明为虚函数,这是因为虚函数需要在运行时进行动态绑定,而类模板在编译时无法确定具体的类型。
template <typename T4>
virtual void show4(T4 data){}
//这个代码是错误的,编译会出错
拷贝构造函数模板
代码示例:
#include <iostream>
template <typename T1>
class C{
public:
C(){
std::cout<<"without parameters"<<std::endl;
}
//模板拷贝构造函数
template<class T2>
C(const C<T2> &c2){
std::cout<<"template copy constructor"<<std::endl;
}
//模板拷贝赋值操作符重载
template<class T3>
C<T1> & operator=(const C<T3> &c3)
{
std::cout<<"template copy assignment"<<std::endl;
return *this;
}
};
int main()
{
C<int> c; //这里是无参数默认构造 输出without parameters
//C<int> c2(c); //这里没有拷贝构造 无输出 ,因为这里c2的模板参数和c一样
//c=c2; //这里没有拷贝赋值 无输出 ,因为这里c2的模板参数和c一样
C<double> c2(c); //这里发生了拷贝构造 输出template copy constructor
c=c2; //这里发生了拷贝赋值 template copy assignment
return 0;
}
知识点:
- 两个模板参数相同的时候,调用无参数默认构造
- 两个模板参数不相同的时候,才会调用模板拷贝构造方法和拷贝赋值操作符