从C开始的CPP学习生活07-模板

1、函数模板

1.1、定义

通过函数重载,实现同名函数,根据不同的参数类型,进行智能调用

1
2
3
4
5
6
7
8
9
void fun(int a)
{
std::cout << a << std::endl;
}

void fun(double d)
{
std::cout << d << std::endl;
}

通过上面的方法,我们在main函数中调用fun函数,传入不同的参数就会调用不同的函数

但这带来的问题是如果参数类型变得更多,会使得fun函数的数量变得过多

C++采用函数模板的方式解决这样的问题,如下所示:

1
2
3
4
5
template<typename T>
void fun(T a)
{
std::cout << a << std::endl;
}

这样我们只需要实现一个函数就可以实现前面多个函数重载实现的功能

这就是函数模板的好处:模板是泛型编程的一种重要思想,stl就是利用模板实现的一个具体实例

1.2、注意事项

1.2.1、函数模板也可以这样编写:

1
template<class T>

1.2.2、函数模板可以有多个参数

1
2
3
4
5
template<typename T, typename Y>
void fun(T a) //ERROR
{
std::cout << a << std::endl;
}

但是要注意,定义了模板之后一定要为其指定类型,上面的代码就是错误的,编译阶段会报错error C2783: 'void fun(T)': could not deduce template argument for 'Y'

1
2
3
4
5
6
7
8
9
10
11
template<typename T, typename Y>
void fun(T a, Y y)
{
std::cout << a << std::endl;
}

int main()
{
fun(12, 16.35f);
return 0;
}

1.2.3、class和typename可以交叉使用

1.2.4、template作用域

template仅对挨着的代码段有效

2、模板的具体化

函数模板的具体化的意思是针对某些特殊类型的数据进行处理,比如类和结构体,也可以对基本数据类型进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Node
{
int a;
double b;
};

template<typename T>
void fun(T a)
{
std::cout << a << std::endl;
}

template<> void fun<Node>(Node no)
{
std::cout << no.a << " " << no.b << std::endl;
}

int main()
{
Node no = { 12, 5.6 };
fun(no);
fun(12);
return 0;
}

2.1、普通函数、函数模板以及模板具体化的优先级

优先级方面:普通函数 > 函数模板的具体化 > 函数模板

即当三种类型的函数都存在时,系统会优先调用普通函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename T>
void fun(T a)
{
std::cout << a << std::endl;
}

template<> void fun<Node>(Node no)
{
std::cout << no.a << " " << no.b << std::endl;
}

void fun(Node no) {

}

系统会有限调用最下面的fun函数

3、函数模板的实例化

模板函数会根据我们传入的不同的参数类型在底层生成不同的函数,我们也可以强制编译器生成某种参数的函数,这就是函数模板的实例化

同时,我们将根据传入参数的类型生成的函数称为隐式实例化,使用强制手法生成的函数叫做显示实例化。如:

1
2
3
4
5
template<typename T>
void fun(T a)
{
std::cout << a << std::endl;
}

当我们调用fun(1.5f)时,编译器会生成float类型的函数

我们使用显示实例化

1
2
3
4
5
6
7
template<typename T>
void fun(T a)
{
std::cout << a << std::endl;
}

template void fun<int>(int a);

我们调用fun(1.5f)时,编译器会生成float和int类型的实例化,因为int类型是显示的实例化

4、类模板

类模板在定义时与函数模板大致相同,调用时则需要指定类名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T>
class CFather
{
public:
T a;
CFather(T t) : a(t) {}
void show()
{
std::cout << a << std::endl;
}
};

int main()
{
CFather<char> pf('k');
return 0;
}

类模板可以指定默认参数

1
2
3
4
5
6
7
8
9
10
11
template<typename T, typename K = int>
class CFather
{
public:
T a;
CFather(T t) : a(t) {}
void show()
{
std::cout << a << std::endl;
}
};

但是,同函数的默认参数一样,类模板的默认参数只能从右向左指定,下面的写法就是错误的

1
template<typename T = char, typename K> //ERROR

4.1、只有类模板可以指定默认值,函数模板不可以

4.2、类模板的类,有默认值时<>里面可以不传参数类型,但是必须有<>

1
CFather<int, char> *k = new CFather<int, char>('1');//指针定义

4.3、类内函数类外定义

1
2
3
4
5
6
7
8
9
10
11
12
template<typename T, typename K>
class CFather
{
public:
T a;
CFather(T t) : a(t) {}
void show();
};

void CFather<char, int>::show() {
std::cout << a << std::endl;
}

我们也可以对show函数使用函数模板,但是注意,函数模板不能设置默认参数

1
2
3
4
template<typename T, typename K>
void CFather<T, K>::show() {
std::cout << a << std::endl;
}

5、继承的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template<typename T, typename K>
class CFather
{
public:
T a;
CFather(T t) : a(t) {}
void show()
{
std::cout << a << std::endl;
}
};

template<typename X, typename Z>
class CSon : public CFather<X, Z>
{
public:
CSon() : CFather<char, int>('1') {}
};

6、多态的模板

子类和父类都有模板列表时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template<typename T, typename K>
class CFather
{
public:
virtual void fun()
{
std::cout << "CFather" << std::endl;
}
};

template<typename X, typename Z>
class CSon : public CFather<X, Z>
{
public:
void fun()
{
std::cout << "CSon" << std::endl;
}
};



int main()
{
CFather<int, char>* pf = new CSon<int, char>;
return 0;
}

当子类没有模板时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template<typename T, typename K>
class CFather
{
public:
virtual void fun()
{
std::cout << "CFather" << std::endl;
}
};


class CSon : public CFather<int, char>
{
public:
void fun()
{
std::cout << "CSon" << std::endl;
}
};



int main()
{
CFather<int, char>* pf = new CSon;
return 0;
}

第二种方式使用的范围太小,所以不推荐使用第二种方式

7、类作为模板的参数

最基本情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CAA
{
public:
int a;
};

template<typename T, typename Y>
class CFather
{
public:
CFather(CAA& ca) {}
void fun()
{
std::cout << "CFather" << std::endl;
}
};

int main()
{
CAA ca;
CFather<int, CAA> pf(ca);
return 0;
}

当我们的类也是一个模板类时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template<typename K>
class CAA
{
public:
int a;
};

template<typename T, typename Y>
class CFather
{
public:
CFather(CAA<char>& ca) {}
void fun()
{
std::cout << "CFather" << std::endl;
}
};

int main()
{
CAA<char> ca;
CFather<int, CAA<char>> pf(ca);
return 0;
}

从C开始的CPP学习生活07-模板
http://example.com/2022/10/26/cppnote07/
作者
Anhongzhan
发布于
2022年10月26日
许可协议