《C++ Primer》笔记,整理关于函数重载与函数匹配的笔记。
函数重载
1 |
|
函数重载有如下的规则:
- 名字相同,形参类型不一样。
- 不允许两个函数除了返回类型外其他所有的要素都相同。
- 顶层
const
的形参无法和没有顶层const
的形参区分。
其中返回类型不同时编译时会出错,而类型别名、项层const
、省略形参名字只是重复声明而已,只要不定义,编译就不会出错,比如:
1 |
|
函数匹配
名字查找
函数匹配的第一步便是名字查找(name lookup),确定候选函数。
名字查找有两方面:
- 常规查找(normal lookup)
- 实参决定的查找(argument-dependent lookup,ADL)
所有函数调用都会进行常规查找,只有函数的实参包括类类型对象或指向类类型对象的指针/引用的时候,才会进行实参决定的查找。
常规查找
1 |
|
从函数被调用的局部作用域开始,逐渐向上层寻找被调用的名字,一旦找到就停止向上寻找,将找到的所有名字加入候选函数。
此外,using语句可以将其他作用域的名字引用到当前作用域。
ADL查找
1 |
|
从第一个类类型参数开始,依次遍历所有类类型参数。对于每一个参数,进入其类型定义所在的作用域(类内友元函数也包括在内),并依次进入其基类、间接基类……定义所在的作用域,查找同名函数,并加入候选函数。
注意:在继承体系中上升的过程中,不会因为找到同名函数就停止上升,这不同于常规查找。
类中的运算符重载也遵循 ADL 查找,其候选函数集既包括成员函数,也应该包括非成员函数。
1 |
|
确定可行函数
第二步便是从候选函数中选出可行函数,选择的标准如下:
- 形参数量与本次调用提供的实参数量相等
- 每个实参的类型与对应的形参类型相同,或者能转换成形参类型
1 |
|
寻找最佳匹配
从可行函数中选择最匹配的函数,如果有多个形参,则最佳匹配条件为:
- 该函数每个实参的匹配都不劣于其他可行函数需要的匹配。
- 至少有一个实参的匹配优于其他可行函数提供的匹配。
否则,发生二义性调用错误。
1 |
|
为了确定最佳匹配,实参类型到形参类型的转换等级如下:
- 精确匹配:
- 实参类型和形参类型相同。
- 实参从数组类型或函数类型转换成对应的指针类型。
- 向实参添加顶层
const
或者从实参中删除顶层const
。
- 通过
const
转换实现的匹配。 - 通过类型提升实现的匹配。
- 通过算术类型转换或指针转换实现的匹配。
- 通过类类型转换实现的匹配。
一般不会存在这个阶段不会同时存在两个以上的精确匹配,因为两个精确的匹配在本质上是等价的,在定义重载函数时,编译器可能就报出重定义的错误了。
挑几个重点的来详细说一下。
指针转换实现的匹配
- 0 或
nullptr
能转换成任意指针类型。 T *
能转换成void *
,const void *
转换成const void*
。- 派生类向基类类型的转换。
- 函数与函数指针的形参类型必须精确匹配。
类类型转换实现的匹配
两个类型提供相同的类型转换将产生二义性问题。
1 |
|
类当中定义了多个参数都是算术类型的构造函数或类型转换运算符,也会产生二义性问题。
1 |
|
当我们使用两个用户定义的类型转换时,如果转换函数之前或之后存在标准类型转换,则标准类型转换将决定最佳匹配到底是哪个。