第八章 函数重载&模板&引用

本文详细介绍了C++中的引用概念,包括常量引用、大数据传递、返回值引用、基类引用、右值引用以及函数重载。引用作为函数参数能提高效率,避免数据复制。同时,右值引用允许处理临时对象。文章还讨论了函数模板、显式具体化以及重载函数的选择策略,强调了类型匹配和模板在函数调用中的作用。
摘要由CSDN通过智能技术生成

引用

引用最大的用处在函数参数中声明变量,使之称为引用传值函数。

  • 当你既想使用引用参数,但却又不希望函数修改引用参数。你可以使用常量引用

    duble refcube(const double &ra){} //你或许应该使用形参
    
  • 当数据比较大时,比如结构或类,传入引用更好。

    • 因为大数据进行复制传参需要的时间很多,使用常量引用会更好。
    • 在大型程序中,引用相比指针更直观也更方便书写
  • 返回值作为引用

    由于教科书中常常出现,所以说明一下。

    引用作为返回值大概率用处只是返回计算值赋值给其他变量,但是这样的工作只是用参形参函数也能解决。

    另一点就是将值赋值给返回值为引用的函数,其实这是一条无效语句。

    free_throw& accumulate(free_throw& target){
    ...
    return target;
    }
    
    accumulate(time)=four;
    //上面的语句相当于
    accumulate(time);
    time = four; //覆盖掉了上一步操作
    
  • 为什么依旧有返回值作为引用的函数

    普通返回将返回值复制到一个临时变量,将临时变脸传递回调用函数,对于大型数据结构,这样的复制时浪费时间。

    另外,在返回引用的时候要注意,避免返回函数返回一个在该函数结束时会释放掉的内存空间,既:

    const free_throw& clone(free_throw& ft){
      free_throw new_item;
      new_item=ft;
      return new_item;
    }
    //同理,也应该避免返回临时变量的指针
    
  • 基类引用可以指向派生类对象,无需强制类型转换。

    这样的好处是,可以定义基类引用的参数,这样可以传递派生类数据。

  • 右值引用

    c++中一个&的引用是左值引用,如果想要出现右值引用,c++11使用&&这个符号.

    我们可以在声明函数的时候使用右值引用参数传递一些变量右值表达式的参数。

int &n=7; //invalid
int &&n=7; //valid
/*---------*/
void staff(int &&value){}

staff(x+y); //valid

函数重载

请看下面两个函数

double cube(double x);
double cube(double& x);

您或许会认为这两个是重载函数,但从编译器的角度看来。假如有一行函数调用

cout<<cube(x);

参数x和引用参数x都符合调用的标准,编译器无法得知该选择哪个进行调用。

但是,常量却不同

void dribble(char *bits);
void dribble(const char *bits);

经验告诉你,将非常量值作为常量传递给函数是正确的,所以编译器同样不会分辨,但事实是编译器确实会分辨常量和非常量的区别。

顺便一提,只有指针和引用会区分,正常的值则不会。

函数模板

函数模板并不会减少可执行程序,最终函数的定义依然取决于你使用了多少不同类型的参数调用模板函数。

模板函数依旧可以重载。

函数模板不能解决所有问题,他不可能比较结构的大小,也不可能让两个数组相加。除非你通过重载运算符的方式解决。

这里解释一下另一种方法——显式具体化

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

上面的语句就是显式具体化函数的声明,它专为job类型的数据定义了一个swap函数,当函数调用时,job数据会优先使用具体化的函数,而不是使用模板生成一个函数定义。

注意:上面的<job>是可选的。

之后讲讲实例化

template<class T>
T add(T a, T b){
  return a+b;
}

int m=6;
double n=5.2;
cout<<add<double>(m,n);

上述中m是int型,n是double正常调用是没用匹配的模板的,这个时候我们可以显式的指定使用double生成的模板。这样m就会类型转换为double。

假如我们把函数定义改成——add(T& a, T& b)

再使用add<double>(m,n)会发现错误,因为实例化生成的double的引用,而无法将int类型转成double的引用。

重载函数的选择

在函数调用的时候,编译器会自动选择最匹配的函数,它是怎么实现的?

  • 首先创建候选函数列表,将所有同名的函数集合起来,包括模板函数。

  • 之后会根据参数数量选出可行的函数列表。

  • 根据数据类型确定最佳函数。其中能够进行数据转换的同样被认为是最佳函数。

    事实上,函数调用的时候有可能出现出现多个重载函数是最佳匹配的情况,通常下,这是一种错误,但是有几个条件是例外。

  • 指向非const数据的指针或引用优先与非const指针和引用匹配。按照下例子,如果参数是非const的引用会优先选择#3,但是非const数据将会出现二义性错误

void recycle(blot);  //#1
void recycle(const blot);   //#2
void recycle(blot&);   //#3
void recycle(const blot&);    //#4
  • 两个模板函数,则优先匹配较为具体的。

    一是显式具体化函数优先匹配,第二种看下面的例子

template <class Type> void recycle(Type t);
template <class Type> void recycle(Type *t);

int ink;
recycle(&ink);
//优先调用第二种,因为Type是int,而第一种Type是int*,所以第二种更具体

decltype

template<class T1, class T2>
void ft(T1 x, T2 y){
...
xgy=x+y;  //what is the xgy type?
...
}

由于x和y的类型有可能不相同,所以编译器无法提前得知xgy的类型,为了解决这种情况,c++增加了关键字decltype。

decltype(x+y) xgy=x + y; //make xgy the same type as x + y

用法是decltype(expression) var

如果expression是函数,则var的类型与返回值相同。当然decltype不会调用函数,只是会检查函数的返回值类型。

如果expression是带括号的左值,那么var为指向其类型的引用,别忘了引用必须初始化。

double ww=1.0;
decltype((ww)) r=ww; //type of r is double&

如果上述条件都不满足,则var为expression的类型。

还有另外一种情况,就是函数返回值类型不明确。

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

我们无法提前得知x和y的类型,所以在生成函数的时候不知道x+y的类型,也就无法使用decltype关键字。这时候,c++新增了使用auto作为占位符,将返回值类型声明在函数后面,变成后置返回类型。

auto gt(T1 x, T2 y) -> decltype(x + y)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值