重载解析

  在C++中,对于函数重载、函数模板和函数模板重载,C++需要有一个良好的策略,去选择调用哪一个函数定义(尤其是多个参数时),这个过程称为重载解析

  (这个过程将会非常复杂,但愿不要遇到一定要写这种代码的时候。)

大致步骤

  Ⅰ.创建候选函数列表(其中包含与候选函数相同名称的函数和模板函数)。

  Ⅱ.使用候选函数列表创建可行函数列表(要求参数数目正确,为此有一个隐式类型转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。例如,使用float参数的函数调用可以将该参数转换为double类型,从而与double匹配,而模板函数可以为float类型生成一个函数实例)。

  Ⅲ.确定是否有最佳的可行函数(如果有则调用,没有则报错)。

  我们以只有一个参数的函数为例:

 may('B');    // 函数调用

 /*以下是一系列候选函数声明*/
void may(int);    // #1
float may(float, float = );        // #2
void may(char);     // #3
char * may(const char *);        // #4
char may(const char &);        // #5
template<class T> void may(const T &); // #6
template<class T> void may(T *); // #7

  这些函数声明都会进入函数列表(因为名称相同),接下来考虑特征标(参数数量与类型),不考虑返回值。其中#4和#7不可行,因为整数无法隐式类型转换为指针类型。#6可用来生成具体化,其中T被替换为char类型,此时还剩下5个可行的函数(#1、#2、#3、#5、#6)。如果此时只剩下一个,那么任何一个都可以正确使用

  

  接下来,到了择优环节。这一步主要考量的是函数调用参数与可行的候选函数的参数匹配所需要进行的转换。通常,从最佳到最差的顺序如下:

   1、完全匹配,函数优于模板。

   2、提升转换(例如,char和short自动转换为int,float自动转换为double)。

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

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

  在剩余的5个函数中,#1优于#2,因为char到int是提升转换,而char到float是标准转换(此时还剩#1,#3,#5,#6)。#3、#5、#6优于#1和#2,因为他们是完全匹配(还剩#3,#5,#6)。#3和#5优于#6,因为#6是模板(还剩#3和#5)。

  这时,会出现两个问题,完全匹配到底是什么?如果有两个完全匹配(#3和#5)该怎么办?通常有两个完全匹配是一种错误,但这一规则有两个例外。

完全匹配和最佳匹配

  进行完全匹配时,C++允许某些“无关紧要的转换”,下表列出了这些转换——Type表示任意类型。例如,int到int &,注意,Type可以是char &这样的类型,因此,这些规则也包括char &到const char &的转换。

完全匹配允许的无关紧要的转换
从实参 到形参
Type Type &
Type &  Type
Type[] * Type
Type(参数列表) Type(*)(参数列表)
Type const Type
Type volatile Type
Type * const Type
Type * volatile Type *

  假设有如下代码:

 struct blot {int a; char b[]};
blot ink = {, "spots"};
recycle(ink); // 下面的原型完全匹配
void recycle(blot); // #1 blot to blot
void recycle(const blot);   // #2 blot to const blot
void recycle(blot &);     // #3 blot to blot &
void recycle(const blot &); // #4 blot to const blot &

  如果有多个完全匹配的原型,则无法完成重载解析过程,如果没有最最佳的可行函数,编译器将报错。

  然而,有这样的例外规则,首先,指向非const数据的指针和引用优先于非const指针和引用,在上例中,如果只定义了#3和#4,将选择#3,因为ink没有被声明为const,然而const和非const之间的区别只适用于指针和引用指向的数据,也就是说,如果只定义了#1和#2,将出现二义性错误。

  一个完全匹配优于另一个的另一种情况是,其中一个是非模板函数而另一个不是,这种情况下,非模板函数将优先于模板函数(包括显式具体化)。

  如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先,这意味着显示具体化将优于模板隐式生成的具体化。

 struct blot {int a; char b[]};
template <class Type> void recycle(Type t); // 模板
template <> void recycle<blot>(blot & t); // 显示具体化 blot ink = {, "spots"};
recycle(ink); //使用显示具体化

  术语“最具体”并不一定意味着显示具体化,而是指编译器推断使用哪种类型时执行的转换最少。例如:

 struct blot {int a; char b[]};
template <class Type> void recycle(Type t); // #1
template <class Type> void recycle(Type * t); // #2 blot ink = {, "spots"};
recycle(&ink); // 使用#2,因为转换最少,#2被认为是更具体的

  用于找出最具体的模板的规则被称为部分排序规则

部分排序规则

 template <typename T>
void show(T arr[], int n); // #1 template <typename T>
void show(T * arr[], int n); // #2 struct debts
{
char name[];
double amount;
}; ...... int things[] = {,,,,,};
debts mr[] =
{
{"aaa", 24.1},
{"bbb", 25.2},
{"ccc", 26.3}
};
double * pd[]; for(int i=; i<; i++)
{
pd[i] = &mr[i].amount;
} show(things, ); // 使用#1
show(pd, );   // 使用#2

  things是一个int数组,与#1匹配,其中T被替换为int。pd是一个double *数组,与#1匹配时,T被替换为double *,与#2匹配时,T被替换为double。在这两个模板中,#2更加具体,因为它做了特定的假设,数组内容是指针,因此被使用。如果将#2从程序中删除,那么使用#1,将显示出地址,而不是值。

  总之,重载解析将寻找最匹配的函数,如果只存在一个这样的函数,则选择它;如果存在多个这样的函数,但其中只有一个非模板函数,则选择它;入伏哦存在多个合适的函数且都为模板函数,但其中只有一个函数比其他函数更具体,则选择它。其他情况(有多个非模板或模板函数,但没有一个比其他更具体,或根本不存在匹配的函数)均为错误。

创建自定义选择

  在有些情况下,可以引导编译器做出你希望的选择。

 template<class T>
T lesser(T a, T b); // #1 int lesser(int a, int b); // #2 ...... int m = ;
int n = -;
double x = 15.5;
double y = 25.9; lesser(m, n); // 使用#2
lesser(x, y); // 使用#1,T被转换为double类型
lesser<>(m, n); // <>提示编译器,使用模板函数,使用#1
lesser<int>(x, y); // 显式实例化,将使用实例化后的函数x,y被强制转换为int类型

多个参数的函数

  将有多个参数的函数调用与有多个参数的原型进行匹配时,情况将非常复杂。编译器必须考虑所有参数的匹配情况。如果找到比其他可行函数都合适的函数,则选择该函数。一个函数要比其他函数都合适,其所有参数的匹配程度都必须不必其他函数差,同时至少有一个参数的匹配程度比其他函数高。

C++ 函数重载,函数模板和函数模板重载,选择哪一个?的更多相关文章

  1. C++ 函数重载、函数模板,类模板

    1.函数重载 相同作用域下,有多个函数名相同,但形参列表不同的函数,常用于处理功能相同但数据类型不同的问题 函数重载的规则: 函数名必须相同 函数形参列表必须不同(可以是参数个数不同,或者数据类型不同 ...

  2. C++ enable_if 模板特化实例(函数返回值特化、函数参数特化、模板参数特化、模板重载)

    1. enable_if 原理 关于 enable_if 原理这里就不细说了,网上有很多,可以参考如下教程,这里只讲解用法实例,涵盖常规使用全部方法. 文章1 文章2 文章3 1. 所需头文件 #in ...

  3. C++ 函数模板和函数重载同时出现如何调用

    C++ 函数模板和函数重载同时出现如何调用 重点 函数模板不允许自动转换,普通函数可以进行自动类型转换 函数模板可以像普通函数一样被重载 C++编译器优先考虑调用普通函数 如果函数模板可以产生一个更好 ...

  4. C++ - 模板类模板成员函数(member function template)隐式处理(implicit)变化

    模板类模板成员函数(member function template)隐式处理(implicit)变化 本文地址: http://blog.csdn.net/caroline_wendy/articl ...

  5. c++的函数模板和类模板

    函数模板和普通函数区别结论: 函数模板不允许自动类型转化 普通函数能够进行自动类型转换 函数模板和普通函数在一起,调用规则: 1 函数模板可以像普通函数一样被重载 2 C++编译器优先考虑普通函数 3 ...

  6. C++模板学习:函数模板、结构体模板、类模板

    C++模板:函数.结构体.类 模板实现 1.前言:(知道有模板这回事的童鞋请忽视) 普通函数.函数重载.模板函数 认识. //学过c的童鞋们一定都写过函数sum吧,当时是这样写的: int sum(i ...

  7. C++_进阶之函数模板_类模板

     C++_进阶之函数模板_类模板 第一部分 前言 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来 ...

  8. C++—模板(1)模板与函数模板

    1.引入 如何编写一个通用加法函数?第一个方法是使用函数重载, 针对每个所需相同行为的不同类型重新实现这个函数.C++的这种编程机制给编程者极大的方便,不需要为功能相似.参数不同的函数选用不同的函数名 ...

  9. C++复习:函数模板和类模板

    前言 C++提供了函数模板(function template).所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表.这个通用函数就称为函数模板.凡是函数体 ...

随机推荐

  1. 用robotframework 标准库String解决由于存在千分位分隔符导致两个数值不相等的问题。

    在编写robotframework自动化断言的过程中,我遇到了如下问题: 我想写一个两个金额判断是否相等的断言,其中一个金额是展示字段存在千分位分隔符,另一个金额是input带入字段,没有千分位分隔符 ...

  2. spark foreachPartition foreach

    1.foreach val list = new ArrayBuffer() myRdd.foreach(record => { list += record }) 2.foreachParti ...

  3. Codeup 25593 Problem G 例题5-7 求圆周率pi的近似值

    题目描述 用如下公式 4*Π = 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 - 1/15 - 求圆周率PI的近似值,直到发现某一项的绝对值小于10-6为止(该项不 ...

  4. 1 - Apache HttpClient 简单使用

    Apache HttpClient 是Apache 开源的实现Http协议的java开源库. HttpClien 是客户端的HTTP通信实现库,实现HTTP GET 和POST请求,获取响应内容. A ...

  5. POJ 跳蚤

    Z城市居住着很多只跳蚤.在Z城市周六生活频道有一个娱乐节目.一只跳蚤将被请上一个高空钢丝的正中央.钢丝很长,可以看作是无限长.节目主持人会给该跳蚤发一张卡片.卡片上写有N+1个自然数.其中最后一个是M ...

  6. Spring Cloud 系列之 Gateway 服务网关(三)

    本篇文章为系列文章,未读第一集的同学请猛戳这里: Spring Cloud 系列之 Gateway 服务网关(一) Spring Cloud 系列之 Gateway 服务网关(二) 本篇文章讲解 Ga ...

  7. java文件上传、下载、图片预览

    多文件保存到本地: @ResponseBody    @RequestMapping(value = "/uploadApp",produces = { "applica ...

  8. vue2.x学习笔记(十一)

    接着前面的内容:https://www.cnblogs.com/yanggb/p/12586416.html. 表单的输入绑定 表单的输入绑定是一块很重要的内容,因为所有的业务系统都离不开基础表单的录 ...

  9. Liunx常用操作(一)-删除命令

    在linux命令行模式下,如何一次性快速删除一行刚刚输入的命令? 经常在命令行输入命令的时候,一段文字都需要删除,一个字段一个字段,比较耗费时间 以下提供一些命令,配合在一起操作,可以一定程度提高工作 ...

  10. RocketMQ存储机制与确认重传机制

    引子 消息队列之前就听说过,但一直没有学习和接触,直到最近的工作流引擎项目用到,需要了解学习一下.本文主要从一个初学者的角度针对RocketMQ的存储机制和确认重传机制做一个浅显的总结. 存储机制 我 ...