C++ 模板初级

引入

在讲函数模板之前就要先讲讲函数重载了

复制代码
#include <iostream>
using namespace std;

int add(int a, int b) {
    return a + b;
}

double add(double a, double b) {
    return a + b;
}

int main() {
    cout << add(2, 3) << endl;     
    cout << add(2.5, 3.7) << endl; 
    return 0;
}

在上面代码我们可以知道除了内置类型不同其他的功能都是一样的,那既然重要功能都一样那我们能不能让它们合二为一,这样就可以处理多种数据类型的情况了,在C++中就可以使用函数模板来解决此类问题

一 函数模板:

概念:

函数模板允许我们编写与类型无关的函数。这样我们就可以在一次编写中处理不同的数据类型,而不必重复编写相同的代码。函数模板使用template关键字定义。

代码示例:

复制代码
#include <iostream>
using namespace std;

template <class T> //也可以 <typename T>
T add(T a , T b)
{
    return a + b;
}

int main() {
    cout << add(2, 3) << endl;
    cout << add(2.5, 3.7) << endl; 
    return 0;
}

这时我们是不是心里面有个疑问,为什么T知道我是什么数据类型,那下面请跟我详细分析

当编译器遇到模板函数的定义时编译器只记录模板的定义,并不会立即生成代码。编译器遇到函数调用add(2, 3):它推断参数23int类型,所以推断模板参数Tint。用int替换T,生成具体函数int add(int a, int b)。在这过程中虽然我们通过代码看到的依然时T但是内部已经全部换成了int类型了,所以说到底还是编译器承受的太多了。

还有一个疑惑就是我又调用了一个double那不是有两个类型了但是上面接收函数只有一个,编译器不会搞混吗?

编译器在编译过程中会生成两个具体的函数,一个用于int类型,一个用于double类型:

复制代码
int add(int a, int b) {
    return a + b;
}

double add(double a, double b) {
    return a + b;
}

因此,编译器会生成两个不同的函数实例,不会有混淆。

总结:

通过模板,我们可以编写更简洁、复用性更高的代码,而编译器会根据具体使用的类型生成对应的函数,不会混淆不同的类型。

模板实例化:

隐式实例化:

隐式实例化是指编译器根据实参推断模板参数的类型,然后实例化模板函数。

代码示例:

复制代码
template <typename T>
T Add(const T& left, const T& right) {
    return left + right;
}

int main() {
    int a1 = 10, a2 = 20;
    double d1 = 10.0, d2 = 20.0;
    Add(a1, a2);  // T 被推断为 int,实例化为 int Add(const int&, const int&)
    Add(d1, d2);
    return 0;
}

显示实例化:

显式实例化是指在调用模板函数时明确指定模板参数的类型。

代码示例:

复制代码
#include <iostream>
using namespace std;

template <class T> //也可以 <typename T>
T add(T a , T b)
{
    return a + b;
}

int main() {
    cout << add<int>(2, 3) << endl;
    cout << add<boudle>(2.5, 3.7) << endl; 
    return 0;
}

总结:

  • 显式实例化:明确指定类型,适用于需要确定类型或编译器无法正确推断类型的情况。
  • 隐式实例化:代码更简洁,适用于编译器能够正确推断类型的情况。

非模板函数与函数模板:

当函数模板跟非函数模板在一起时,那咋们该怎么样调用呢?

  • 完全匹配的非模板函数:编译器首先会查找非模板函数,如果找到与参数完全匹配的非模板函数,就会优先选择它。
  • 匹配的模板函数:如果没有找到完全匹配的非模板函数,编译器会尝试使用模板函数进行匹配。模板函数的匹配过程包括模板参数推断和模板实例化。

代码示例:

复制代码
#include <iostream>
using namespace std;

// 专门处理 int 类型的加法函数
int Add(int left, int right) {
    return left + right;
}

// 处理 double 类型的加法函数
double Add(double left, double right) {
    return left + right;
}

// 通用加法函数模板
template <typename T1, typename T2>
auto Add(T1 left, T2 right) -> decltype(left + right) {
    return left + right;
}

void Test() {
    cout << Add(1, 2) << endl;          // 调用非模板函数 Add(int, int)
    cout << Add(2.5, 3.5) << endl;      // 调用非模板函数 Add(double, double)
    cout << Add(1, 2.0) << endl;        // 调用模板函数 Add<int, double>(int, double)
}

int main() {
    Test();
    return 0;
}

总结

  • 非模板函数优先:如果有一个非模板函数的参数类型完全匹配,编译器会优先选择它。
  • 模板函数其次:如果没有找到完全匹配的非模板函数,编译器会尝试使用模板函数进行匹配,并实例化一个具体的函数。
  • 模板的灵活性:模板函数可以接受不同类型的参数,并生成对应的具体函数,提供了更大的灵活性。

二 类模板:

概念:

类模板是C++中的一种机制,用于创建通用类,允许类的成员使用不特定的数据类型。类模板可以使代码更加通用和可重用,避免为每种数据类型编写重复的代码。

定义:

复制代码
template <typename T>
class Box {
public:
    Box(T value) : _value(value) {}
private:
    T _value;
};
int main() {
    Box<int> intBox(123);           // 实例化 Box,T 为 int
    Box<double> doubleBox(45.67);
}

那在类模板中可不可以隐式实例化呢?

对于类模板,编译器不能自动推断模板参数的类型,必须显式指定。而对于函数模板,编译器确实可以根据传递的参数自动推断模板参数的类型。

对于类模板我们先讲这些到后面咋们慢慢补上

相关推荐
科比不来it13 分钟前
Go语言数据竞争Data Race 问题怎么检测?怎么解决?
开发语言·c++·golang
给大佬递杯卡布奇诺38 分钟前
FFmpeg 基本API av_seek_frame函数内部调用流程分析
c++·ffmpeg·音视频
uxiang_blog1 小时前
C++进阶:重载类型转换
linux·开发语言·c++
moringlightyn2 小时前
c++11可变模版参数 emplace接口 新的类功能 lambda 包装器
开发语言·c++·笔记·其他·c++11·lambda·包装器
郝学胜-神的一滴2 小时前
使用Linux系统函数递归遍历指定目录
linux·运维·服务器·开发语言·c++·软件工程
会开花的二叉树3 小时前
C++微服务 UserServer 设计与实现
开发语言·c++·微服务
我星期八休息3 小时前
C++智能指针全面解析:原理、使用场景与最佳实践
java·大数据·开发语言·jvm·c++·人工智能·python
·白小白3 小时前
力扣(LeetCode) ——11.盛水最多的容器(C++)
c++·算法·leetcode
深思慎考4 小时前
RabbitMQ 入门:基于 AMQP-CPP 的 C++ 实践指南与二次封装
开发语言·c++·分布式·rabbitmq·api
深思慎考4 小时前
Ubuntu 系统 RabbitMQ 安装指南与使用(含 C++ 客户端与 SSL 错误解决)
c++·ubuntu·rabbitmq·github·rabbitmqpp