C++基础系列【21】模板初探

发布于:2025-03-10 ⋅ 阅读:(14) ⋅ 点赞:(0)

博主介绍:程序喵大人

学C++的朋友应该都知道多态,多态分为运行时多态和编译时多态,其中模板可以粗浅理解为是编译时多态的其中一种方式。

模板是一种通用的代码框架,允许在编写代码时使用占位符(通常是类型或值),在实际使用时再指定具体的类型或值。使用模板进行泛型编程,可以认为是用一种无关于特定类型的方式来编写代码。

模板大体分为函数模板与类模板,其中还有些主要的特性,比如全特化、偏特化、SFINAE等。

模板的声明以关键字template开头,后跟模板参数列表。模板参数可以是类型参数(typenameclass,现在一般都是使用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中还引入了变量模板,但是不常用,感兴趣的可以看

推荐阅读

参考链接

以上链接需要的请关注文末!!

练习

  1. 实现一个通用的Pair类模板,包含两个成员变量firstsecond,并提供构造函数和访问方法。
  2. 实现一个通用的Stack类模板,支持pushpoptop操作。
  3. 实现一个通用的sort函数模板,支持对任意类型的数组进行排序。

码字不易,欢迎大家点赞关注评论,谢谢!


C++训练营

专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!训练营介绍