天天看点

C++函数模板(泛型编程一)

  在C语言中为了代码复用可以定义宏代码块,但宏代码块在预编译的时候被整体替换掉了,编译器并不知道宏的存在,故缺少类型检查。另宏也不支持递归调用。在C++中为了代码复用便运用泛型编程思想。C++用模板来实现泛型编程,模板分为函数模板和类模板。

1.函数模板定义

  a.函数模板是一种特殊的函数,可用不同类型进行调用;

    

  b.看起来和普通函数很相似,区别是类型可被参数化。 

2.函数模板语法规则

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

  template关键字用于声明开始进行泛型编程;

  typename关键字用于声明泛指类型。

 3.函数模板的使用

  

  a.自动推导调用

int a = ;
int b = ;

Swap(a, b);
           

  上述代码中编译器会自动推导泛指类型T为int。

  b.具体类型显示调用

float c = ;
float d = ;

Swap<float>(c, d);
           

  上述代码显示的指明泛指类型T为float。

下面代码给出函数模板的初步使用:

#include <iostream>
#include <string>

using namespace std;

template < typename T >
void Swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

template < typename T >
void Sort(T a[], int len)
{
    for(int i=; i<len; i++)
    {
        for(int j=i; j<len; j++)
        {
            if( a[i] > a[j] )
            {
                Swap(a[i], a[j]);
            }
        }
    }
}

template < typename T >
void Println(T a[], int len)
{
    for(int i=; i<len; i++)
    {
        cout << a[i] << ", ";
    }

    cout << endl;
}

int main()
{
    int a[] = {, , , , };

    Println(a, );
    Sort(a, );
    Println(a, );

    string s[] = {"Java", "C++", "Pascal", "Ruby", "Basic"};

    Println(s, );
    Sort(s, );
    Println(s, );

    return ;
}
           

运行结果如下所示:

C++函数模板(泛型编程一)

4.编译器如何编译函数模板

  a.编译器从函数模板通过具体类型产生不同的函数;

  

  b.编译器会对函数模板进行两次编译,首先对模板代码本身进行编译,然后对参数替换后的代码进行编译。

  c.函数模板本身不允许隐式类型转换。当采用自动推导类型时,必须严格匹配;在显示类型指定时,能够进行隐式类型转换。

下面分析如下代码:

#include <iostream>
#include <string>

using namespace std;

class Test
{
    Test(const Test&);
public:
    Test()
    {
    }
};

template < typename T >
void Swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

typedef void(FuncI)(int&, int&);
typedef void(FuncD)(double&, double&);
typedef void(FuncT)(Test&, Test&);

int main()
{
    FuncI* pi = Swap;    // 编译器自动推导 T 为 int
    FuncD* pd = Swap;    // 编译器自动推导 T 为 double
    // FuncT* pt = Swap;    // 编译器自动推导 T 为 Test

    cout << "pi = " << reinterpret_cast<void*>(pi) << endl;
    cout << "pd = " << reinterpret_cast<void*>(pd) << endl;
    // cout << "pt = " << reinterpret_cast<void*>(pt) << endl;

    return ;
}
           

运行结果如下所示:

C++函数模板(泛型编程一)

  从上图显示结果来看,指针pi和pd的值不相等,这便证明编译器会对函数模板进行两次编译,且根据不同的泛指类型产生不同的函数。

5.多参数函数模板

  函数模板可以定义任意多个不同的类型参数,如下所示:

template <typename T1, typename T2, typename T3>
T1 Add(T2 a, T3 b)
{
    return static_cast<T1>(a + b);
}
           

  对于多参数的函数模板和一个参数的使用方法一样,但如果返回值类型也是泛指类型时编译器无法自动推导返回值类型,必须显示指定。在显示指定参数类型时,不一定要全部指定,可以从左到右部分指定类型参数,下面给出示例:

#include <iostream>
#include <string>

using namespace std;

template 
< typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
    return static_cast<T1>(a + b);
}


int main()
{
    // T1 = int, T2 = double, T3 = double
    int r1 = Add<int>(, );

    // T1 = double, T2 = float, T3 = double
    double r2 = Add<double, float>(, );

    // T1 = float, T2 = float, T3 = float
    float r3 = Add<float, float, float>(, );

    cout << "r1 = " << r1 << endl;     // r1 = 1
    cout << "r2 = " << r2 << endl;     // r2 = 1.3
    cout << "r3 = " << r3 << endl;     // r3 = 1.3

    return ;
}
           

运行结果如下所示:

C++函数模板(泛型编程一)

6.重载函数模板

  函数模板可以像普通函数一样被重载,有记下几点需要注意:

  

  a.C++编译器优先考虑普通函数;

  

  b.如果函数模板可以产生一个更好的匹配,那么选择模板;

  

  c.可以通过空模板实参列表限定编译器只匹配模板。

分析如下代码:

#include <iostream>
#include <string>

using namespace std;

template < typename T >
T Max(T a, T b)
{
    cout << "T Max(T a, T b)" << endl;

    return a > b ? a : b;
}

int Max(int a, int b)
{
    cout << "int Max(int a, int b)" << endl;

    return a > b ? a : b;
}

template < typename T >
T Max(T a, T b, T c)
{
    cout << "T Max(T a, T b, T c)" << endl;

    return Max(Max(a, b), c);
}

int main()
{
    int a = ;
    int b = ;

    cout << "Max(a, b) = " << Max(a, b) << endl << endl;                   // 普通函数 Max(int, int)

    cout << "Max<>(a, b) = " << Max<>(a, b) << endl << endl;               // 函数模板 Max<int>(int, int)

    cout << "Max(3.0, 4.0) = " << Max(, ) << endl << endl;           // 函数模板 Max<double>(double, double)

    cout << "Max(5.0, 6.0, 7.0) = " << Max(, , ) << endl << endl; // 函数模板 Max<double>(double, double, double)

    cout << "Max('a', 100) = " << Max('a', ) << endl << endl;           // 普通函数 Max(int, int)

    return ;
}
           

运行结果如下所示:

C++函数模板(泛型编程一)

7.函数模板特化

  现分析下面代码:

  

template <typename T>
bool equal(T a, T b)
{
    return (a == b);
}
           

上面所示代码为比较两个对象之间是否相等,请看下面的调用:

cout << equal(, ) << endl;
cout << equal(, ) << endl;
           

会以int为泛指类型来调用函数模板,可以输出正确的结果,但如果用下面的方式调用:

cout << equal(, ) << endl;
           

会以double为泛指类型来调用函数模板,但double型在内存中存储方式并非精确存储,所以比较两个double型数不能用“==”来比较,故需要提供一个函数特化,如下所示:

template < >
bool Equal<double>(double a, double b)
{
    const double delta = ;
    double r = a - b;

    return (-delta < r) && (r < delta);
}
           

继续阅读