🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️
本博客致力于分享知识,欢迎大家共同学习和交流。
C++除了有面向对象的编程思想,还有泛型编程的思想,泛型编程可以体现在C++的模版上。模板(Templates)是一种强大的泛型编程工具,允许我们去编写通用的类和函数,以支持多种数据类型,而无需重复编写代码。模板可以分为函数模板和类模板两种主要类型。
在C++98添加关键字typename之前,C++使用关键字class来创建模板。
如果考虑向后兼容,函数模板应使用typename,而不是class。
函数模板实例化可以让编译器自动推导,也可以在调用的代码中显式的指定。
函数模板(Function Templates)
函数模板(Function Template)是C++中一种特殊的机制,允许我们编写通用的函数定义,这些函数可以用于多种不同的数据类型,而无需为每种数据类型编写单独的函数。 比如说,我们通常写的函数都是具体有形参类型,返回类型,里面具体数据类型的。类型参数化可以在我们写函数时将这些数据类型变成未知的变量,从而得到一个函数模板,我们可以根据传入不同的数据类型得到不同的结果和实现。
函数模板允许定义一个函数,其中某些类型可以作为参数进行通用化。定义函数模板使用 template
关键字和 <...>
括号来指定泛型参数。
语法:template <typename T>
示例:
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int sumInt = add<int>(3, 5); // 实例化为 int 类型的 add 函数
double sumDouble = add<double>(1.1, 2.2); // 实例化为 double 类型的 add 函数
cout << "int两数之和: " << sumInt << endl;
cout << "double两数之和: " << sumDouble << endl;
return 0;
}
如何得到具体的函数?
编译器自动推导
add(a, b);
上面表示让编译器根据我们传进去的数据自己推导T应该成为什么数据类型。
显式的指定
add<int>(a, b);
上面表示显式指定数据类型,我们可以在实参列表前加上声明指定T的数据类型。
函数模板的注意事项
1)可以为类的成员函数创建模板,但不能是虚函数和析构函数。
2)使用函数模板时,必须明确数据类型,确保实参与函数模板能匹配上。
template<typename T>
void add()
{
}
int main()
{
//Swap();//错误
add<int>();
return 0;
}
3)使用函数模板时,推导的数据类型必须适应函数模板中的代码。
不能模板传递不同的数据类型,这样编译器会不知道选择哪一种数据类型。
template<typename T>
void add(T& a, T& b)
{
}
int main()
{
char a = 'a';
int b = 20;
add(a,b);
return 0;
}
4)使用函数模板时,如果是自动类型推导,不会发生隐式类型转换,如果显式指定了函数模板的数据类型,可以发生隐式类型转换。
5)函数模板支持多个通用数据类型的参数。
函数模板支持重载,并且可以与非通用数据类型的参数一起使用。
- 规则1:当普通函数和函数模板都可以调用时,优先调用普通函数。
- 规则2:可以在函数调用时加上空的模板参数列表
<>
来强制调用函数模板。例如:add<>(...)
。 - 规则3:函数模板可以重载,即不同模板函数可以使用相同的函数名。
- 规则4:如果有更好的匹配,编译器会根据更好的匹配优先调用函数模板。
#include <iostream>
using namespace std;
void add(int a) {
cout << "普通函数" << endl;
}
template<typename T>
void add(T a) {
cout << "函数模板" << endl;
}
template<typename T>
void add(T a, T b) {
cout << "函数模板2" << endl;
}
int main() {
add(100); // 调用普通函数,规则1
add<>(100); // 调用函数模板,规则2
add(100, 200); // 传入两个参数,优先匹配调用函数模板2,模板函数发生重载,规则3,4
return 0;
}
add(100);
调用普通函数,因为规则1优先匹配普通函数。add<>(100);
强制调用函数模板,因为规则2使用空模板参数列表。add(100, 200);
传入两个参数,调用重载的函数模板2,因为规则3和规则4使得编译器选择更好的匹配。
类模板(Class Templates)
类模板与函数模板的作用类似,也需要在类定义的上面加上template和typename,但在定义对象时,必须使用<>显式的指定模版类型
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
下面是一个简单的类模板的创建:
#include<iostream>
using namespace std;
template<class G, class T>
class Student
{
public:
Student(G a, T b)
{
this->m_age = a;
this->m_name = b;
}
G m_age;
T m_name;
};
int main()
{
Student<int, string> A(20, "蔡徐坤");
cout <<"年龄:" << A.m_age << ",姓名:" << A.m_name << endl;
return 0;
}
类模版的注意事项
注意:
1)在创建对象的时候,必须指明具体的数据类型。
2)使用类模板时,数据类型必须适应类模板中的代码。
3)类模板可以为通用数据类型指定缺省的数据类型(C++11标准的函数模板也可以)。
4)模板类的成员函数可以在类外实现。
5)可以用new创建模板类对象。
6)在程序中,模板类的成员函数使用了才会创建。
模版类具体化
模板类具体化(也称特化、特例化)有两种形式:完全具体化和部分具体化。具体化程度高的类优先于具体化程度低的类,具体化的类优先于未具体化的类。具体化的模板类中,成员函数在类外实现的代码应放在源文件中。
示例程序:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
// 类模板
template<class T1, class T2>
class MathOperations { // 类模板
public:
T1 a;
T2 b;
MathOperations(const T1 a, const T2 b) : a(a), b(b) {
cout << "类模板:构造函数。\n";
}
void add() const {
cout << "类模板:加法结果 = " << a + b << endl;
}
void multiply() const {
cout << "类模板:乘法结果 = " << a * b << endl;
}
};
// 类模板完全具体化
template<>
class MathOperations<int, double> {
public:
int a;
double b;
MathOperations(const int a, const double b) : a(a), b(b) {
cout << "完全具体化:构造函数。\n";
}
void add() const {
cout << "完全具体化:加法结果 = " << a + b << endl;
}
void multiply() const {
cout << "完全具体化:乘法结果 = " << a * b << endl;
}
};
// 类模板部分具体化
template<class T1>
class MathOperations<T1, int> {
public:
T1 a;
int b;
MathOperations(const T1 a, const int b) : a(a), b(b) {
cout << "部分具体化:构造函数。\n";
}
void add() const {
cout << "部分具体化:加法结果 = " << a + b << endl;
}
void multiply() const {
cout << "部分具体化:乘法结果 = " << a * b << endl;
}
};
int main()
{
// 具体化程度高的类优先于具体化程度低的类,具体化的类优先于未具体化的类
MathOperations<int, double> op1(3, 4.5); // 将使用完全具体化的类
MathOperations<char, int> op2('A', 10); // 将使用部分具体化的类
MathOperations<int, float> op3(7, 2.5); // 将使用模板类
op1.add();
op1.multiply();
op2.add();
op2.multiply();
op3.add();
op3.multiply();
return 0;
}
我们定义了一个通用的类模板 MathOperations
,并提供了两个具体化版本:
- 完全具体化:为
MathOperations<int, double>
提供了一个特化版本。 - 部分具体化:为
MathOperations<T1, int>
提供了一个特化版本,其中T1
可以是任意类型。
在 main
函数中:
MathOperations<int, double> op1(3, 4.5);
使用了完全具体化的类版本,并调用了add
和multiply
方法,输出对应的结果。MathOperations<char, int> op2('A', 10);
使用了部分具体化的类版本,并调用了add
和multiply
方法,输出对应的结果。MathOperations<int, float> op3(7, 2.5);
使用了通用的类模板版本,并调用了add
和multiply
方法,输出对应的结果。
模板类与继承
1)模板类继承普通类(常见)。
#include <iostream>
using namespace std;
class Base {
public:
int m_base;
Base(int base) : m_base(base) { cout << "调用了Base的构造函数。\n"; }
void showBase() { cout << "调用了showBase()函数:m_base = " << m_base << endl; }
};
template<class T1, class T2>
class MathOperations : public Base {
public:
T1 m_x;
T2 m_y;
MathOperations(const T1 x, const T2 y, int base) : Base(base), m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};
int main() {
MathOperations<int, string> mo(8, "我是一个参数。", 3);
mo.show();
mo.showBase();
return 0;
}
2)普通类继承模板类的实例化版本。
#include <iostream>
using namespace std;
template<class T1, class T2>
class MathOperations {
public:
T1 m_x;
T2 m_y;
MathOperations(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};
class Derived : public MathOperations<int, string> {
public:
int m_base;
Derived(int base, int x, string y) : MathOperations(x, y), m_base(base) { cout << "调用了Derived的构造函数。\n"; }
void showBase() { cout << "调用了showBase()函数:m_base = " << m_base << endl; }
};
int main() {
Derived d(3, 8, "我是一个参数。");
d.show();
d.showBase();
return 0;
}
3)普通类继承模板类。(常见)
#include <iostream>
using namespace std;
template<class T1, class T2>
class MathOperations {
public:
T1 m_x;
T2 m_y;
MathOperations(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};
template<class T1, class T2>
class Derived : public MathOperations<T1, T2> {
public:
int m_base;
Derived(int base, const T1 x, const T2 y) : MathOperations<T1, T2>(x, y), m_base(base) { cout << "调用了Derived的构造函数。\n"; }
void showBase() { cout << "调用了showBase()函数:m_base = " << m_base << endl; }
};
int main() {
Derived<int, string> d(3, 8, "我是一个参数。");
d.show();
d.showBase();
return 0;
}
4)模板类继承模板类。
#include <iostream>
using namespace std;
template<class T1, class T2>
class MathOperations {
public:
T1 m_x;
T2 m_y;
MathOperations(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};
template<class T, class T1, class T2>
class AdvancedMathOperations : public MathOperations<T1, T2> {
public:
T m_z;
AdvancedMathOperations(const T z, const T1 x, const T2 y) : MathOperations<T1, T2>(x, y), m_z(z) { cout << "调用了AdvancedMathOperations的构造函数。\n"; }
void showAdvanced() { cout << "调用了showAdvanced()函数:z = " << m_z << endl; }
};
int main() {
AdvancedMathOperations<int, int, string> amo(3, 8, "我是一个参数。");
amo.show();
amo.showAdvanced();
return 0;
}
5)模板类继承模板参数给出的基类(不能是模板类)。
#include <iostream>
using namespace std;
class Base1 {
public:
Base1() { cout << "调用了Base1的构造函数。\n"; }
Base1(int a) { cout << "调用了Base1的构造函数Base1(int a)。\n"; }
};
class Base2 {
public:
Base2() { cout << "调用了Base2的构造函数。\n"; }
Base2(int a) { cout << "调用了Base2的构造函数Base2(int a)。\n"; }
};
template<class T>
class MathOperations : public T {
public:
MathOperations() : T() { cout << "调用了MathOperations的构造函数MathOperations()。\n"; }
MathOperations(int a) : T(a) { cout << "调用了MathOperations的构造函数MathOperations(int a)。\n"; }
};
int main() {
MathOperations<Base1> mo1;
MathOperations<Base2> mo2(5);
return 0;
}
模板类与函数
模板类可以用于函数的参数和返回值,有三种形式:
1)普通函数,参数和返回值是模板类的具体实例。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Test {
public:
T1 m_x;
T2 m_y;
Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
void display() const { cout << "display() x = " << m_x << ", y = " << m_y << endl; }
};
// 普通函数,参数和返回值是模板类Test的具体实例。
Test<int, string> process(Test<int, string>& mo) {
mo.display();
cout << "调用了process(Test<int, string> &mo)函数。\n";
return mo;
}
int main() {
Test<int, string> mo(3, "this is a test");
process(mo);
return 0;
}
2) 函数模板,参数和返回值是某种模板类
#include <iostream>
using namespace std;
template<class T1, class T2>
class Test {
public:
T1 m_x;
T2 m_y;
Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
void display() const { cout << "display() x = " << m_x << ", y = " << m_y << endl; }
};
// 函数模板,参数和返回值是模板类Test的某种类型。
template <typename T1, typename T2>
Test<T1, T2> process(Test<T1, T2>& mo) {
mo.display();
cout << "调用了process(Test<T1, T2> &mo)函数。\n";
return mo;
}
int main() {
Test<int, string> mo(3, "this is a test");
process(mo);
return 0;
}
3) 函数模板,参数和返回值是任意类型
#include <iostream>
using namespace std;
template<class T1, class T2>
class Test {
public:
T1 m_x;
T2 m_y;
Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
void display() const { cout << "display() x = " << m_x << ", y = " << m_y << endl; }
};
// 函数模板,参数和返回值是任意类型。
template <typename T>
T process(T &mo) {
mo.display();
cout << "调用了process(T &mo)函数。\n";
return mo;
}
int main() {
Test<int, string> mo(3, "this is a test");
process(mo);
return 0;
}
模板类与友元
模板类的友元函数有三种类型:
1)非模板友元:友元函数不是模板函数,而是利用模板类参数生成的具体函数。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Test {
T1 m_x;
T2 m_y;
public:
Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
friend void show(const Test<T1, T2>& a) {
cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
}
};
int main() {
Test<int, string> a(88, "this is a test");
show(a);
Test<char, string> b('A', "this is a test");
show(b);
}
2)约束模板友元
模板类实例化时,每个实例化的类对应一个友元函数。
#include <iostream>
using namespace std;
template <typename T>
void show(T& a);
template<class T1, class T2>
class Test {
friend void show<>(Test<T1, T2>& a);
T1 m_x;
T2 m_y;
public:
Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template<class T1, class T2>
class OtherTest {
friend void show<>(OtherTest<T1, T2>& a);
T1 m_x;
T2 m_y;
public:
OtherTest(const T1 x, const T2 y) : m_x(x), m_y(y) { }
}
template <typename T>
void show(T& a) {
cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <>
void show(Test<int, string>& a) {
cout << "具体Test<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <>
void show(OtherTest<int, string>& a) {
cout << "具体OtherTest<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main() {
Test<int, string> a1(88, "this is a test");
show(a1); // 使用具体化版本
Test<char, string> a2('A', "this is a test");
show(a2); // 使用通用版本
OtherTest<int, string> b1(88, "this is a test");
show(b1); // 使用具体化版本
OtherTest<char, string> b2('B', "this is a test");
show(b2); // 使用通用版本
}
3)非约束模板友元
模板类实例化时,会生成多个友元函数,每个实例化的类都拥有多个友元函数。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Test {
template <typename T> friend void show(T& a);
T1 m_x;
T2 m_y;
public:
Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template <typename T>
void show(T& a) {
cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <>
void show(Test<int, string>& a) {
cout << "具体<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main() {
Test<int, string> a(88, "this is a test");
show(a); // 使用具体化版本
Test<char, string> b('A', "this is a test");
show(b); // 使用通用版本
}