C++函数模板声明和实现

重载函数使编程变得方便,因为对于执行类似操作的一组函数,只要记住一个函数名称即可。但是,每个函数都必须单独编写。例如,来看以下重载的 square 求平方函数:
int square(int number)
{
    return number * number;
}
double square(double number)
{
    return number * number;
}
这两个函数之间的唯一区别是它们的返回值及其形参的数据类型。在这种情况下,编写函数模板比重载函数更方便。函数模板允许程序员编写一个单独的函数定义,以处理许多不同的数据类型,而不必为每个使用的数据类型编写单独的函数。

函数模板不是实际的函数,而是编译器用于生成一个或多个函数的 "模具"。在编写函数模板时,不必为形参、返回值或局部变量指定实际类型,而是使用类型形参来指定通用数据类型。当编译器遇到对函数的调用时,它将检查其实参的数据类型,并生成将与这些数据类型配合使用的函数代码。

以下是一个 square 函数的函数模板:
template<class T>
T square(T number)
{
    return number * number;
}
函数模板由一个模板前缀标记开始,该模板前缀以关键字 template 开始,接下来是一组尖括号,里面包含一个或多个在模板中使用的通用数据类型。通用数据类型以关键字 dass 开头,后面跟着代表数据类型的形参名称。

在上面的 square 函数模板示例中,只使用了一个名为 T 的形参(如果有更多的话,它们将用逗号分隔)。在此之后,除了使用类型形参代替实际的数据类型名称之外,其他的都可以像往常一样写入函数的定义。在本函数模板示例中,以下是其函数头:

T square(T number)

其中,T 是类型形参或通用数据类型。该函数头定义了一个 square 函数,它返回一个 T 类型的值,并使用了一个形参 number,这也是 T 类型的数字。

如前所述,编译器会检查对 square 的每次调用,并以适当的数据类型填充 T,例如,以下调用将使用 int 参数:

int y, x = 4;
y = square(x);

以上代码将导致编译器生成以下函数:
int square(int number)
{
    return number * number;
}
但是,如果使用以下语句调用 square 函数:

double y, d = 6.2;
y = square(d);

那么编译器生成的函数将如下所示:
double square(double number)
{
    return number * number;
}
下面的程序演示了该函数模式的用法:
// This program uses a function template.
#include <iostream>
#include <iomanip>
using namespace std;

// Template definition for square function
template <class T>
T square(T number)
{
    return number * number;
}

int main()
{
    cout << setprecision(5);
    //Get an integer and compute its square
    cout << "Enter an integer: ";
    int iValue;
    cin >> iValue;
    // The compiler creates int square(int) at the first occurrence of a call to square with an int argument
    cout << "The square is " << square(iValue);
    // Get a double and compute its square
    cout << "\nEnter a double: ";
    double dValue;
    cin >> dValue;
   
    // The compiler creates double square(double)at the first
    // occurrence of a call to square with a double argument
    cout << "The square is " << square (dValue) << endl;
    return 0;
}
程序输出结果:

Enter an integer: 3
The square is 9
Enter a double: 8.3
The square is 68.89

注意,函数模板中定义的所有类型形参必须在函数的形参列表中至少出现一次。


图 1 通过函数模板生成的两个函数实例

由于编译器在上面的程序中遇到了对 square 函数的两次调用,并且每次都使用了不同的形参类型,所以它生成了两个函数实例的代码(如图 1 所示):
  • 一个带有 int 形参和 int 返回类型的函数实例;
  • 一个带有 double 形参和 double 返回类型的函数实例;

请注意,在上边的程序中,模板出现在所有对 square 函数的调用之前。与常规函数一样,当编译器遇到对模板函数的调用时,必须已经知道模板的内容。因此,模板应放在程序的顶部附近或头文件中。

注意,函数模板仅仅是函数的规范,本身并不会导致使用内存。当编译器遇到对模板函数的调用时,才会在内存中创建该函数的实际实例。