持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天, 点击查看活动详情
在我们开始内容之前,让我们想一想为什么需要模板,模板给我们带来什么,我们学习面向对象编程时,都学到面向对象 3 个,其中一个就是多态,可以通过继承于一个基类不同子类来实现多态
模板中静态成员变量
在 C++ 类中,通过类上的静态成员。有这么一个情景就是,就是我们需要对象的计数器,这个成员变量需要由类来维护,在构造函数中对其增加,来记录创建了多少个对象,在析构函数中将其减少来。
template<typename T>
class MyClass
public:
MyClass(T val){
val_ = val;
void print(){
cout << "Value: " << val_ << endl;
private:
T val_;
int main()
MyClass<int> i1(1);
i1.print();
cout<<"Hello World";
return 0;
上面我们创建了一个不用类模板,接下来就需要在个类模板上添加一个静态变量
template <typename T>
class MyClass {
public:
MyClass(T val) {
val_ = val;
object_count++;
~MyClass() {
object_count--;
void print() {
std::cout << "Value: " << val_ << std::endl;
static unsigned int object_count;
static void printObjectCount() {
std::cout << "Object count: " << object_count << std::endl;
private:
T val_;
template <typename T>
unsigned int MyClass<T>::object_count = 0;
int main()
MyClass<int> i1(4);
MyClass<int> i2(5);
MyClass<int> i3(7);
MyClass<int>::printObjectCount();
cout<<"Hello World";
return 0;
我们需要值得注意的是在类模板中,只允许声明一个静态成员,而不能对其进行定义。如果想要定义可以static const unsigned int object_count = 0;
int main()
MyClass<int> i1(4);
MyClass<int> i2(5);
MyClass<float> f1(0.5);
MyClass<std::string> s1("string1");
MyClass<std::string> s2("string2");
MyClass<std::string> s3("string3");
MyClass<int>::printObjectCount();
MyClass<float>::printObjectCount();
MyClass<std::string>::printObjectCount();
return 0;
#include <iostream>
#include <cstdlib>
#include <string>
class MyClassBase {
public:
MyClassBase() {
object_count++;
~MyClassBase() {
object_count--;
static unsigned int object_count;
static void printObjectCount() {
std::cout << "Object count: " << object_count << std::endl;
unsigned int MyClassBase::object_count = 0;
template <typename T>
class MyClass : public MyClassBase {
public:
MyClass(T val) {
val_ = val;
~MyClass() {
void print() {
std::cout << "Value: " << val_ << std::endl;
private:
T val_;
int main()
MyClass<int> i1(4);
MyClass<int> i2(5);
MyClass<float> f1(0.5);
MyClass<std::string> s1("string1");
MyClass<std::string> s2("string2");
MyClass<std::string> s3("string3");
MyClassBase::printObjectCount();
return 0;
对于成员函数定义为了避免语法复杂性给我们带来麻烦,可以将成员函数定义都在类模板中进行定义,而不是在类模板以外进行定义。
在 c++17 版本中类模板中,无法定义一个静态成员变量,而在 C++17 之后,允许使用 inline
关键字来定义静态成员变量。
模板参数推断
当我们在调用函数模板时,有时候可以和调用一个函数类似,编译器会根据传入参数类型进行推断 T
类型,然后将占位符 T
替换成指定的类型。
template<typename T>
T MyAbs(T t)
if (t < 0)
return -t;
return t;
可以调用函数模板就和调用重载函数类似,如下MyAbs(-)
无需指定MyAbs<int>
,其实编译器是足够聪明将调用补全MyAbs<int>(-1)
这才是函数模板正确的调用姿势。
int main()
int res = MyAbs(-1);
std::cout << res << std::endl;
return 0;
编译器足够聪明可以推断出 T
的参数为引用类型
template<typename T>
void swap(T &a, T &b)
T temp(a);
a = b;
b = temp;
template<typename T>
void swap2(T &a, T &b)
T temp(a);
a = b;
b = temp;
int main()
int a = 1;
int b = 2;
int* p = &a;
int* q = &b;
swap2(a,b);
swap2(p,q);
return 0;
而且编译器也可以推断出一些位于复合类型中的类型例如
template<typename T>
void f(T const *a[]){
//do something
int main(){
char const *names[] = {"RNN","CNN","LSTM",nullptr};
f(names);
return 0;
这里提出的是char const *name[]
和参数类型结构是匹配的 T const *a[]
这时即使返回值类型已经确定给出了 int res
,模板可以根据模板参数来推断出返回值时,需要在调用模板函数时给出类型,所以像下面这样调用模板函数是会
int res = f();
对于类模板调用时需要指定类型参数
需要在调用函数模板时指定类型参数的值,这样调用才有效。
int res = f<int>();
template<typename T>
class Rational
public:
Rational(T n ):num_(n),den_(0){}
private:
T num_;
T den_;
int main()
Rational rationalInt(1);
return 0;
template<typename T>
class Rational
public:
Rational():num_(0),den_(1){};
Rational(T n ):num_(n),den_(1){};
private:
T num_;
T den_;
int main()
Rational r1,r2;
return 0;
对于类模板我们需要声明时需要给类型参数的值,即便Rational r2(1);
像这样给出了参数,可以根据给出参数来推测类型,也需要给出类型参数来声明类 Rational<int> r2(1);
Rational<int> r1(1);
Rational r2(r1);
也需要像这样Rational<int> r2(r1);
给出类型参数来声明一个 Rational 的对象,不过在 c++17
之后,Rational r2(r1)
这样来声明也是合法的。
template<typename T>
T foo(T val){
//do something
复制代码