C++泛型编程与函数模板

本文详细介绍了C++中的函数模板,包括定义、格式、作用域、编译阶段以及实例化过程。讨论了模板形参,包括类型形参的转换规则和非类型形参的概念与用途。此外,还探讨了模板函数的重载和特化,解释了何时以及如何进行特化以应对特定类型的需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

一、函数模板

1、定义:

代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

2、格式

template<typename/classT>//但是建议使用typename,避免和class类混淆

Template <类型1变量1, 类型2 变量2, ….. > 返回类型 函数名(形参表)

{

    函数定义体;

}

 

3、作用域

typedef intT;

template <typenameT>

T Add(Tleft,Tright)

{

    T value = 3.14;//此处的valuedouble类型,局部隐藏了typedef定义的全局T;

    cout<< typeid(value).name() << endl;

    return(left+right);

}//T的作用域在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则。

 

T global ;此处的T是全局的typedef的 int T;

 

4、编译:

模板被一共被编译了两次:

第一阶段:实例化之前,检查模板代码本身,看看模板是否出现语法错误,如:忘记分号或者变量名拼错,并检查参数类型是否匹配,在此阶段,编译器只能检查部分的错误。

第二阶段:实例化期间,只有这个阶段才会发现类型相关的错误,例如:实例化类型不支持某些函数调用。

5、实例化:

template <typenameT>

T Add(T left, T right)

{

    return(left+ right);

}

cout << Add('1', '2')<<endl;//c

cout << Add<int>(1, '1') <<endl;//49+1=50

cout << Add(1, '1') << endl;//编译不通过

 

 

二.模板形参:(类型形参和非类型形参)

类型形参:

1、类型形参转换:( 编译器只会执行两种转换)

(1)const转换:接受const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用。

template <typenameT>

void FunTest(constT&p)//本来应该是const int *p而实际是int*const p

{

int b = 10;

p = &b;//编译错误,试图修改p的值

cout << *p << endl;

}

(2)数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当做指向第一个元素的指针,函数实参当做指向函数类型的指针.

void FunTest1()

{

    cout<< "FunTest()"<< endl;

}

template <typenameT>

void FunTest(T p)

{

    cout<< typeid(p).name() << endl;

}

int arr[10];

FunTest(arr);//运行结果:int*

FunTest(FunTest1);//运行结果:void <__cdecl*><void>

 

 

注意:

1、模板形参的名字在同一模板形参列表中只能出现一次。

2、模板形参用<>括起来,且模板形参不能为空,多个形参之间必须加“,”。

3、模板形参可以是类型形参和非类型形参,类型形参必须使用关键字class或typename。

4、使用模板类型形参和内置类型或者自定义类型使用方法完全一样,可用作指定函数形参类型,返回类型,局部变量和强转类型转换。

 

非模板类型参数:

1、概念:

非类型模板参数,顾名思义,模板参数不限定于类型,普通值也可作为模板参数。它是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型。而使用基于常量的模板时,你必须显式地指定这些值,模板方可被实例化。

2、用途:

一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或引用。

绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。

3、例如:

template<typenameT,int n>

void funtest(T(&array)[n])

{

    for(inti = 0; i < n; i++)

    {

       array[n]= 0;

    }

}

 

template<unsignedN,unsigned M>

int compare(constchar(&p1)[N],constchar(&p2)[M])

{

    returnstrcmp(p1, p2);

}

    int_array[5];

    funtest(_array);

    /*

   000434FC  call        funtest<int,5> (041528h) 

    */

    char_array1[5];

    funtest(_array1);

    /*

    00234518  call       funtest<char,5> (023152Dh)

    */

    compare("hi", "hello");

    /*编译器会按照字面常量的大小来代替NM,从而实例化模板,

    注意:

    编译器会在一个字符串字面常量的末尾插入一个空字符作为终结符,

    因此编译器会实例化处以下版本:

    intcompare(const char(&p1)[3], const char(&p2)[6])

    Ps:非类型模板参数的模板必须是常量表达式。

    */

 

三、模板函数重载

类似于通函数一样,模板函数也可以重载。(注意:模板函数既不是函数也不是类)

int _max(intleft,int right)

{

    returnleft >right ? left : right;

}

 

template <typenameT>

T _max(const T&left, const T & right)

{

    cout<< typeid(left).name() << endl;

    cout<< typeid(right).name() << endl;

 

    returnleft >right?left:right;

}

template <typenameT>

T _max(T left, T mid,T right)

{

    T temp =_max(left, mid);

    returntemp>right?temp:right;

}

int main()

{

    _max(3,5);//优先调用非模板函数,而不会从该模板产生一个实例

    _max<>(3,5);//显示指定一个空的模板实参列表,告诉编译器只能是模板才能匹配这个调用

    _max<>(3,5, 6); //调用的是模板函数,_max(3, 5)构成重载

    _max(2,4.5);//输出4,编译器调用的是非模板函数(普通函数),

                //因为模板函数不允许自动类型转换,但是普通函数可以进行自动类型转换

     _max<int>(2,4.5);//输出4,调用的是模板函数,显示将函数参数都转换为int类型

    //_max<>(3,4.3 ) ;//编译不通过,模板函数只有一个T类型,而模板尝试实例化这个函数时,却遇到两个不同类型参数,

                     //模板不允许自动类型转换,所以报错

    return0;

}

 

四、模板函数特化

1、原因:

当函数模板需要对某些类型进行特别处理,称为函数模板的特化。因为,在某些情况下,通用模板定义对于某个类型可能是错误的,或者编译通过不了,即使编译通过了,也不是我们想要得到的答案,这时候,需要我们对函数模板进行特化。

2、特化格式:

template<>

返回值  函数名<type> (函数参数列表){函数体 }

3、例如:

template<typenameT>//普通函数模板

int is_Equal(T t1, T t2)

{

    if(t1 < t2) return -1;

    if(t1>t2) return 1;

    return0;

 

}

 

template<>//特化函数模板

int is_Equal<constchar*>(constchar*constp1,constchar*constp2)

{

    returnstrcmp(p1, p2);

}

    const char*p1 = "bbbb";

    const char*p2 = "aaaa";

    cout<< is_Equal( p1, p2) << endl;//p1>p2应该返回1,而实际结果却是-1

                                      //p1 0x0108dc70 "bbbb" char *

                                      //p2 0x0108dc78 "aaaa" char *

    //比较时,直接比较的是两个地址的大小,而没有比较两个指针的内容

 

4、特化位置:

特化必须出现在模板实例的调用之前,否则在编译的时候,找不到特化函数模板,则会按照普通模板函数进行调用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值