C++函数模板详解(一):概念和特性

本文介绍了C++中的函数模板,通过一个返回最大值的模板函数示例,阐述了如何定义和使用函数模板。模板参数用`template`关键字声明,允许使用任意类型,只要类型支持模板中所需的操作。模板实例化发生在编译期间,针对每种类型生成独立函数,若类型不支持模板操作则会导致编译错误。
摘要由CSDN通过智能技术生成

一、定义一个简单的函数模板

下面的这个例子就定义了一个模板函数,它会返回两个参数中最大的那一个:

<span style="color:#333333"><code><span style="color:#008000">// 文件:"max.hpp"</span>
<span style="color:#0000ff">template</span><<span style="color:#0000ff">typename</span> T>
<span style="color:#0000ff">inline</span> <span style="color:#0000ff">const</span> T& <span style="color:#a31515">max</span>(<span style="color:#0000ff">const</span> T& x, <span style="color:#0000ff">const</span> T& y)
{
    <span style="color:#0000ff">return</span> x < y ? y : x;
}</code></span>

这个函数模板定义了一个“返回两个值中最大者”的函数家族,而参数的类型还没有确定,用类型模板参数T来确定。模板参数需要使用如下的方式来声明:

<span style="color:#333333"><code><span style="color:#0000ff">template</span>< 模板参数列表 ></code></span>

在这个例子中,模板参数列表为:typename T。关键字typename引入了T这个类型模板参数。当然了,可以使用任何标识符作为类型模板参数的名称。我们可以使用任何类型(基本数据类型、类类型)来实例化该函数模板,只要所使用的数据类型提供了函数模板中所需要的操作即可。例如,在这个例子中,类型T需要支持operator <,因为a和b就是通过这个操作符来比较大小的。

鉴于历史原因,也可以使用关键字class来取代typename来定义类型模板参数,然而应该尽可能地使用typename

二、使用函数模板

下面的程序使用了上面定义的这个函数模板:

<span style="color:#333333"><code><span style="color:#2b91af">#include <iostream></span>
<span style="color:#2b91af">#include <string></span>
<span style="color:#2b91af">#include "max.hpp"</span>

<span style="color:#0000ff">using</span> <span style="color:#0000ff">namespace</span> <span style="color:#0000ff">std</span>;

<span style="color:#0000ff">int</span> <span style="color:#a31515">main</span>(<span style="color:#0000ff">int</span> argc, <span style="color:#0000ff">char</span> *argv[])
{
    <span style="color:#0000ff">cout</span> << max(4, 3) << <span style="color:#0000ff">endl</span>; <span style="color:#008000">// 使用int类型实例化了函数模板,并调用了该函数实例。</span>
    <span style="color:#0000ff">cout</span> << max(4.0, 3.0) << <span style="color:#0000ff">endl</span>; <span style="color:#008000">// 使用double类型实例化了函数模板,并调用了该函数实例。</span>
    <span style="color:#0000ff">cout</span> << max(<span style="color:#0000ff">string</span>(<span style="color:#a31515">"hello"</span>), <span style="color:#0000ff">string</span>(<span style="color:#a31515">"world"</span>)) << <span style="color:#0000ff">endl</span>; <span style="color:#008000">// 使用string类型实例化了函数模板,</span>
                                                           <span style="color:#008000">// 并调用了该函数实例。</span>
    <span style="color:#0000ff">return</span> 0;
}</code></span>

通常而言,并不是把模板编译成一个可以处理任何类型的单一实体,而是针对于实例化函数模板参数的每种类型,都从函数模板中产生出一个独立的函数实体。因此,针对于每种类型,模板代码都被编译了一次。这种用具体类型代替模板参数的过程,叫做模板的实例化。它产生了一个新的函数实例(与面向对象程序设计中的实例化不同)。

如果试图基于一个不支持模板内部所使用的操作的类型实例化一个模板,那么将会引发一个编译期错误:

<span style="color:#333333"><code><span style="color:#0000ff">std</span>::<span style="color:#0000ff">complex</span><<span style="color:#0000ff">double</span>> c1, c2;
max(c1, c2); <span style="color:#008000">// 编译错误:std::complex并不支持运算符<</span></code></span>

所以说:模板被编译了两次,分别发生于:

  • 模板实例化之前,查看语法是否正确,此时可能会发现遗漏的分号等。
  • 模板实例化期间,检查模板代码, 查看是否所有的调用都有效。此时可能会发现无效的调用,例如实例化类型不支持某些函数调用等。

所以这引发了一个重要的问题:当使用函数模板并且引发模板实例化时,编译器必须查看模板的定义。事实上,这就不同于普通的函数,因为对于普通的函数而言,只要有函数的声明(甚至不需要定义),就可以顺利地通过编译期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值