博主介绍:程序喵大人
- 35- 资深C/C++/Rust/Android/iOS客户端开发
- 10年大厂工作经验
- 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
- 《C++20高级编程》《C++23高级编程》等多本书籍著译者
- 更多原创精品文章,首发gzh,见文末
- 👇👇记得订阅专栏,以防走丢👇👇
😃C++基础系列专栏
😃C语言基础系列
😃C++大佬养成攻略
😃C++训练营
学C++的朋友应该都知道多态,多态分为运行时多态和编译时多态,其中模板可以粗浅理解为是编译时多态的其中一种方式。
模板是一种通用的代码框架,允许在编写代码时使用占位符(通常是类型或值),在实际使用时再指定具体的类型或值。使用模板进行泛型编程,可以认为是用一种无关于特定类型的方式来编写代码。
模板大体分为函数模板与类模板,其中还有些主要的特性,比如全特化、偏特化、SFINAE等。
模板的声明以关键字template
开头,后跟模板参数列表。模板参数可以是类型参数(typename
或class
,现在一般都是使用typename
)或非类型参数(如整数、指针等)。
这篇文章主要介绍下函数模板与类模板的使用。
函数模板
函数模板与类模板的声明比较类似,都是使用template
关键字,还是直接看示例代码吧。
template <typename T>
T Max(T const& a, T const& b) {
return a > b ? a : b;
}
int main() {
std::cout << Max(1, 2) << std::endl;
std::cout << Max(1.2, 2.3) << std::endl;
std::cout << Max(std::string("1.2"), std::string("2.3")) << std::endl;
}
T
起到一个类似占位符的作用,使用时传入的是什么类型,它就会规定T
就是这个类型。
template
用于声明这是一个模板,typename
用来表明T
是一个泛型类型。也有很多模板的声明不是使用typename
,而是使用的class
,区别不大。
大体语法如下:
template<typename type>
ret-type funcname(parameter list) {
// function
}
类模板
再举一个类模板的示例:
#include <iostream>
#include <vector>
template <typename T>
class Queue {
public:
void Push(const T& t) { elems.emplace_back(t); }
T& Front() { return elems.front(); }
void PopFront() { elems.erase(elems.begin()); }
size_t Size() { elems.size(); }
bool Empty() { return elems.empty(); }
private:
std::vector<T> elems;
};
int main() {
Queue<int> q;
q.Push(1);
q.Push(2);
while (!q.Empty()) {
std::cout << q.Front() << std::endl;
q.PopFront();
}
std::cout << "=========== \n";
Queue<float> q2;
q2.Push(1.1);
q2.Push(2.1);
while (!q2.Empty()) {
std::cout << q2.Front() << std::endl;
q2.PopFront();
}
}
觉得上面的示例复杂,可以看下面,这个示例可能更简单一些:
#include <iostream>
#include <vector>
template <typename P, typename Q>
class A {
public:
A(P p, Q q) : p_(p), q_(q) {}
void Print() { std::cout << "p " << p_ << " q " << q_ << std::endl; }
private:
P p_;
Q q_;
};
int main() {
A<int, double> a(1, 3.3);
a.Print();
A<int, std::string> b(2, "dsds");
b.Print();
}
这里可以看到,类模板的格式类似于下面这样:
template <typename type>
class classname {};
再看下默认模板参数:
就像函数可以有默认参数一样,模板也可以有默认参数,比如:
#include <iostream>
#include <string>
template <typename P, typename Q = int>
class A {
public:
A(P p, Q q) : p_(p), q_(q) {}
void Print() { std::cout << "p " << p_ << " q " << q_ << std::endl; }
private:
P p_;
Q q_;
};
int main() {
A<int, double> a(1, 3.3);
a.Print();
A<int, std::string> b(2, "dsds");
b.Print();
A<int> c(1, 2);
c.Print();
}
模板实例化
模板的实例化是指编译器根据模板生成具体的函数或类的过程。模板实例化可以是隐式的(由编译器自动完成)或显式的(由程序员指定)。
隐式实例化
int a = max(3, 5); // 编译器隐式实例化max<int>
double b = max(3.5, 2.5); // 编译器隐式实例化max<double>
显式实例化
template int max<int>(int, int); // 显式实例化max<int>
template double max<double>(double, double); // 显式实例化max<double>
模板特化
模板的特化是指为特定的类型或值提供特殊的实现。分为全特化和偏特化。
- 全特化:模板的所有参数指定具体的类型或值。
- 偏特化:模板的部分参数指定具体的类型或值。
看模板全特化和偏特化的示例代码。
函数模板全特化:
#include <iostream>
#include <string>
template <typename T>
T Max(T const& a, T const& b) {
std::cout << "Max a b \n";
return a > b ? a : b;
}
template <>
int Max(int const& a, int const& b) {
std::cout << "Max a b full specialization \n";
return a > b ? a : b;
}
int main() {
std::cout << Max(1, 2) << std::endl;
std::cout << Max(1.2, 2.3) << std::endl;
std::cout << Max(std::string("1.2"), std::string("2.3"));
}
输出:
Max a b full specialization
2
Max a b
2.3
Max a b
2.3
类模板全特化
template <>
class A<std::string, std::string> {
public:
A(std::string p, std::string q) : p_(p), q_(q) {}
void Print() { std::cout << "std::string p " << p_ << " q " << q_ << std::endl; }
private:
std::string p_;
std::string q_;
};
int main() {
A<int, double> a(1, 3.3);
a.Print();
A<int, std::string> b(2, "dsds");
b.Print();
A<std::string, std::string> c("hello", "dsds");
c.Print();
}
类模板偏特化
template <typename P>
class A<P, std::string> {
public:
A(P p, std::string q) : p_(p), q_(q) {}
void Print() { std::cout << "partial specialization p " << p_ << " q " << q_ << std::endl; }
private:
P p_;
std::string q_;
};
注意:
- 类模板可以全特化,也可以偏特化
- 函数模板只能全特化,不可以偏特化,想要达到偏特化效果,直接使用函数重载就好了。
- 编译器优先匹配特化的类型,然后才会找通用模板做匹配。
- 这里只介绍了基础的模板操作,模板还有很多高级玩法,比如成员函数模板,内部类模板等,不过这些都不常见,感兴趣的可以看
- 还有个有意思的知识点,不只可以为普通类型使用using起别名,其实还可以给模板起别名,也是使用using,具体可以看
- 除了函数模板和类模板,在C++14中还引入了变量模板,但是不常用,感兴趣的可以看
推荐阅读
参考链接
以上链接需要的请关注文末!!
练习
- 实现一个通用的
Pair
类模板,包含两个成员变量first
和second
,并提供构造函数和访问方法。 - 实现一个通用的
Stack
类模板,支持push
、pop
和top
操作。 - 实现一个通用的
sort
函数模板,支持对任意类型的数组进行排序。
码字不易,欢迎大家点赞,关注,评论,谢谢!
C++训练营
专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!训练营介绍