C++学习笔记:函数重载及函数模板

函数重载

       默认参数能让你使用不同数目的参数调用同一个函数,而函数多态(函数重载)能让你使用多个同名函数。----一般完成类似的工作,但一定使用不同的参数列表(函数特征标)。

       下面定义一组原型如下的print()函数

void print(const char*str,int width);
void print(double d,int width);
void print(long l,int width);

        编译器根据参数列表选择相应原型。

注意事项

        1.类型引用和类型本身,const和非const变量(非指针,引用)被视为同一个特征标。

        2.实现函数重载时,函数的返回类型可以不同,但特征标也必须不同。

        3.若出现参数匹配多个函数版本,调用最匹配的版本。

函数模板

        函数模板是通用的函数描述,它们使用泛型来定义函数,其中泛型可用具体的类型(例如int,double等)替换。下面都将以Swap()函数为例解释相关知识。

标准格式

 例

template
void Swap

        关键字typelate,typename必须包含,类型名(这里是AnyType)可以任意选择,一般用T。

typename也可以替换为class,它们是等价的。

        编译器将检查所使用的参数类型,并生成相应的函数。

模板的重载

 例

template
void Swap(T &a,T &b)
{
    T temp;
    temp=a;
    a=b;
    b=temp;
}

template
void Swap(T a[],T b[],int n)
{
    T temp;
    for(int i=0;i

        模板的参数类型不一定都是泛型

模板的具体化与实例化

实例化

隐式实例化

        编译器使用模板为特定类型生成函数定义时,得到的是模板实例,这种实例化方式称为隐式实例化。

int m,n;
m=1,n=2;
Swap(m,n);

此时编译器根据提供的参数类型,生成了对应的函数定义。

显式实例化

        可以直接命令编译器创建特定的实例,如Swap()。语法是用符号<>指示类型,并在声明前加上关键字template。

template void Swap(int,int);

        还可在程序中使用函数来创建显示实例化 。

template 
T Add(T a,T b)
{
    return a+b;
}
...
int m=6;
double x=10.1;
cout<(x,m)<

         模板与函数调用不匹配,因为x,m不是相同类型的变量。但通过使用Add(x,m),可强制为double类型实例化,并将参数m强制转化为double类型。

显式具体化

        与实例化不同,该种方法不使用Swap()模板来生成函数定义,而是专门为一个具体类型生成函数定义。两个等价声明为

template <> void Swap(int &,int &);
template <> void Swap(int &,int &);

         隐式实例化,显式实例化,显式具体化统称为具体化。它们表示的都是使用具体化类型的函数定义,而不是通用描述。

编译器选择使用哪个函数版本

       对于函数重载,函数模板,函数模板重载,C++需要一个好的策略来决定为函数调用使用哪一个函数定义。下面将根据单个参数的函数简要介绍相关原则,通常从最佳到最差的顺序如下所述

        1.完全匹配,但常规函数优先于模板。

        2.提升转换(例如,char或short自动转换为int,float自动转换为double)

        3.标准转换(例如,int转换为char,long转换为double)

        4.用户定义的转换,如声明中定义的转换。

may('B')//函数调用,参数为char类型
//下面为系列函数原型
void may(int)               //#1
float may(float,float=3)    //#2
void may(char)              //#3
char*may(const char*);      //#4
char may(const char&);      //#5
template void may(const T &);   //#6
template void may(T *);    //#7

        #4和#7类型不匹配排除。根据原则1,#6为模板优先级低于常规函数。根据原则2和3 char到int为提升转换,char到float为标准转换,#1优于#2。#3,#5,#6都优先于#2,#1,因为它们都是完全匹配的。

完全匹配和最佳匹配

        匹配时允许一些"无关紧要的转换",在这些转换内都是完全匹配。

从实参 到形参
Type Type&
Type& Type
Type[] *Type
Type(argument-list) Type(*)(argument list)
Type const Type
Type volatile Type
Type* const Type
Type* voliatile Type*

                两个函数都完全匹配时,仍然可完成重载解析。

  1. 指向非const指针和引用优先与非const的指针和和引用参数匹配。(仅对于引用,指针,其他类型会被视为二义性错误)
  2. 优先级:非模板函数>模板(具体化)函数>模板(非具体化)函数。

          越具体不一定针对模板,实际为编译器判断使用哪种类型执行的转换最少。

创建自定义选择

有时候可以编写合适的函数调用来引导编译器做出你希望的选择。

int a,b;
a=1;b=2;
//下面为函数调用
cout<(a,b)<(a,b)<

        #1中<>指出编译器应使用模板函数而不是非模板函数。

        #2中指出编译器应使用显式实例化得到的函数。

关键字decltype

        编写模板函数时,一个问题并非总能预先知道该在声明中使用哪种类型。如下例

template
void ft(T1 x,T2 y)
{
?type? xpy=x+y;//x+y的类型无法确定
}

        T1,T2分别是double,int,两个变量的和将为double类型;是short,int,变量的和将为int类型;是short,char,变量的和将为int类型。

        C++11新增的关键字decltype提供了解决方案,可以这样使用

int x;
decltype(x) y;//让y的类型与x相同
decltype(x+y) xpy;//让xpy的类型与x+y相同
xpy=x+y
//也可样写
decltype(x+y) xpy=x+y;

        确定类型编译器必须遍历一个核对表,简化步骤如下

decltype(expression)var//声明

1.expression是未用括号括起的标识符(变量名,函数名等),则var与该标识符同类型。

2.expression为函数调用,则var类型与返回类型相同。

3.expression是用括号括起的标识符,则var为指向其类型的引用。例如\

double x=4.4;
decltype((x)) r2=x;//r2是指向double变量x的引用

4.前面情况都不满足则var与expression同类型。(例如表达式,右值)

C++后置返回类型

        有一相关问题decltype本身无法解决。

template
?type? gt(T1 x,T2 y)
{
    return x+y;
}

        返回类型无法设置为decltype(x+y),因为此时还未声明x和y。而C++后置返回类型写成这样

double h(int x,float y);
//改用后置返回类型
auto h(int x,float y)->double;
//则上面的gt()函数可改为
template
auto gt(T1 x,T2 y)->decltype(x+y)
{
    return x+y;
}

        其中auto是一个占位符,表示后置返回类型提供的类型。

你可能感兴趣的:(c++,学习,笔记)