C++模板基础

C++语法——模板基础

观前提醒

本人刚学C++不久,超级无敌大萌新。如有错误,请立即指出。
本人全程使用C++20进行编写测试

引入

swap()是非常常用的函数,使用过程中我们会发现,实参不管是什么类型,只要是相同的类型,它们就能交换。

int main()
{
    int a = 1, b = 2;
    swap(a, b); // a == 2, b == 1
    char c = 'A', d = 'B';
    swap(c, d); // c == 'B', d == 'A'
    std::string a = " ", b = "\n";
    swap(a, b); // a == "\n", b == " "
    struct Node { int q, p; }e = { 1,2 }, f = { 3,4 };
    swap(e, f); // e == {3,4}, f == {1,2}
    return 0;
}

这是如何实现的?下面是一种方法

void swap(int& a, int& b)
{
    int t = a;
    a = b;
    b = t;
}
void swap(char& a, char& b)
{
    char t = a;
    a = b;
    b = t;
}
void swap(std::string& a, std::string& b)
{
    std::string t = a;
    a = b;
    b = t;
}

但是有一个明显的缺陷,如果有100个类型就要重载100个swap函数,那么有什么其他的方法呢?且听我慢慢道来。

auto类型

auto在C++11中被赋予了新的含义,也就是我们现在用的。
auto有多种用法。
auto的主要作用就是自动推导类型。
看以下代码演示
右侧是一个int类型,auto会自动判断出右侧的类型,所以aint类型。
任何一个auto都需要初始值,否则会因为无法推导其类型而报错。
来看以下代码

int main()
{
	auto a = 1;
	auto b = std::string(); // OK, std::string b
	auto c = 1LL; // OK, long long c
	auto d; // 无法推导d的类型(需要初始值),非法
	return 0;
}

auto不得滥用,大部分情况该用什么类型就写什么类型。以下是auto的使用场景。

1. 自动推导类型,简化一些复杂的类型,比如一些迭代器
int main()
{
	std::vector<int> v = { 1,2,3,4,5 };
	for (std::vector<int>::iterator it = v.begin(); it != v.end(); it++) {}
	for (auto it = v.begin(); it != v.end(); it++) {}

	for (int it : v) {}
	for (auto it : v) {}
	// 这四句话的效果是一样的
	return 0;
}

解释一下后两行的for ,这两行的for和1、2行的for,本质上是一样的。1、2行的写法已经被淘汰了。要注意,3、4行的for中,·it前面写的不是迭代器类型,而是int

2. 函数返回类型自动推导(C++14)
auto max(int a, int b)
{
	return (a > b ? a : b);
}
int max(int a, int b)
{
	return (a > b ? a : b);
}

这两段代码是等价的。编译器自动推导了返回值的类型。

3.简写函数模板(C++20)

具体的函数模板在后面。

auto max(const auto& a, const auto& b)
{
	return (a > b ? a : b);
}

形参的两个auto都可以是任意类型(只不过下面return的值会报错),这里ab的值取决于实参。

auto f(const auto& a, const auto& b) {};

int main()
{
	f(3, std::vector<int>{}); //用{}初始化为空
	// OK, 三个auto推导出不同的类型
	// 第一个auto推导出void
	// 第二个auto推导出int,a类型为const int&
	// 第三个auto推导出vector<int>,b的类型为const vector<int>&
	return 0;
}

要注意的是,auto推导出的类型不含引用和cv修饰符(即const和volatile)

函数模板

函数模板的声明与定义

模板分很多,这里主要讲函数模板
上文提到了,使用auto进行简化函数模板,但依旧会出现问题:不能保证一个形参或几个形参推到后是同一个类型。接下来我们看看标准的函数模板长什么样。

template < 形参列表 > 函数声明

这就是一个模板,一般函数声明那里要换行。
下面就是一个max()函数的简单实现 (STL里完全不是这么写的)

template<typename T>
T max(T a, T b)
{
	return (a > b ? a : b);
}

为了更好理解,我把一些修饰符去掉了。
模板的形参列表中,先写一个typename,再写一个形参的名字,这里的名字是T,当然也可以是别的名字比如orz,但T比较常用。这里的T可以被推导,但max的返回值,ab的类型都是T

· 先说说实例化:在函数模板中,指根据模板参数在需要时生成特定类型或值的代码。还是上面那个例子。
如果你调用了max(1,2),通过推导,Tint,那么编译器就会生成一个int版本的max()

int max(int a, int b)
{
	return (a > b ? a : b);
}

类型被推导后,就会直接将T替换为int
如果加上修饰符,那么推导后的类型也会修饰。

template<typename T> //函数模板
T max(const T& a, const T& b) 
{
	return (a > b ? a : b);
}

int max(const int& a, const int& b) //实例化
{
	return (a > b ? a : b);
}

此外,实例化还可以手动自己写,这里不再赘述。

其他注意事项:

· 形参列表里可以有多个形参,比如

template<typename T1, typename T2>
T1 f(T1 a, T2 b) {}

· 一般地, 一个模板只管一个函数
· 模板里的形参可以在函数体的内部使用,比如

template<typename T>
void swap(const T& a, const T& b)
{
	T t = a;
	a = b;
	b = t;
}

关于函数模板的调用

其实函数模板调用时是可以手动指定推导的类型
f<类型>(实参列表)
看以下几个例子

template<typename T>
void swap(const T& a, const T& b){}

int main()
{
	int a = 0;
	int b = 1;
	swap(a, b); // OK, 完全由编译器推导
	swap<int>(a, b); // OK, 手动指定类型
	swap<double>(a, b); // OK, 隐式转换为double类型
	swap<std::string>(std::string{}, std::string{}); // OK, 两个string
	swap<std::string>(a, b); // 报错,a和b是int,int不能直接转换为string
	return 0;
}

参考文献

https://zh.cppreference.com/w/cpp/language/auto

https://zh.cppreference.com/w/cpp/language/templates

  • 38
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值