重载函数参数的转换和匹配

为了确定最佳匹配,编译器将实参类型到相应形参类型转换划分等级。转换等级以降序排列如下:

1、精确匹配。实参与形参类型相同。

2、通过类型提升实现的匹配(第 5.12.2 节)。

3、通过标准转换实现的匹配(第 5.12.3 节)。

4、通过类类型转换实现的匹配(第 14.9 节将介绍这类转换)。

类型提升或转换适用于实参类型可通过某种标准转换提升或转换为适当的形参类型情况。

必须注意的一个重点是较小的整型提升为 int 型。假设有两个函数,一个的形参为 int 型,另一个的形参则是 short 型。对于任意整型的实参值,int 型版本都是优于 short 型版本的较佳匹配,即使从形式上看 short 型版本的匹配较佳:

  void ff(int);
  void ff(short);
  ff('a'); // char promotes to int, so matches f(int)

字符字面值是 char 类型,char 类型可提升为 int 型。提升后的类型与函数 ff(int) 的形参类型匹配。char 类型同样也可转换为 short 型,但需要类型转换的匹配“劣于”需要类型提升的匹配。结果应将该调用解释为对 ff (int) 的调用。

通过类型提升实现的转换优于其他标准转换。例如,对于 char 型实参来说,有 int 型形参的函数是优于有 double 型形参的函数的较佳匹配。其他的标准转换也以相同的规则处理。例如,从 char 型到 unsigned char 型的转换的优先级不比从 char 型到 double 型的转换高。再举一个具体的例子,考虑:

  extern void manip(long);
  extern void manip(float);
  manip(3.14); // error: ambiguous call

字面值常量 3.14 的类型为 double。这种类型既可转为 long 型也可转为 float 型。由于两者都是可行的标准转换,因此该调用具有二义性。没有哪个标准转换比其他标准转换具有更高的优先级。


回顾枚举类型 enum,我们知道这种类型的对象只能用同一枚举类型的另一个对象或一个枚举成员进行初始化(第 2.7 节)。整数对象即使具有与枚举元素相同的值也不能用于调用期望获得枚举类型实参的函数。

  enum Tokens {INLINE = 128, VIRTUAL = 129};
  void ff(Tokens);
  void ff(int);
  int main() {
  Tokens curTok = INLINE;     //????????
  ff(128); // exactly matches ff(int)
  ff(INLINE); // exactly matches ff(Tokens)
  ff(curTok); // exactly matches ff(Tokens)
  return 0;
  }

传递字面值常量 128 的函数调用与有一个 int 型参数的 ff 版本匹配。

虽然无法将整型值传递给枚举类型的形参,但可以将枚举值传递给整型形参。此时,枚举值被提升为 int 型或更大的整型。具体的提升类型取决于枚举成员的值。如果是重载函数,枚举值提升后的类型将决定调用哪个函数:

  void newf(unsigned char);
  void newf(int);
  unsigned char uc = 129;
  newf(VIRTUAL); // calls newf(int)
  newf(uc); // calls newf(unsigned char)

枚举类型 Tokens 只有两个枚举成员,最大的值为 129。这个值可以用 unsigned char 类型表示,很多编译器会将这个枚举类型存储为 unsigned char 类型。然而,枚举成员 VIRTUAL 却并不是 unsigned char 类型。就算枚举成员的值能存储在 unsigned char 类型中,枚举成员和枚举类型的值也不会提升为 unsigned char 类型。


在使用有枚举类型形参的重载函数时,请记住:由于不同枚举类型的枚举常量值不相同,在函数重载确定过程中,不同的枚举类型会具有完全不同的行为。其枚举成员决定了它们提升的类型,而所提升的类型依赖于机器。

仅当形参是引用或指针时,形参是否为 const 才有影响。

可基于函数的引用形参是指向 const 对象还是指向非 const 对象,实现函数重载。将引用形参定义为 const 来重载函数是合法的,因为编译器可以根据实参是否为 const 确定调用哪一个函数:

  Record lookup(Account&);
  Record lookup(const Account&); // new function
  const Account a(0);
  Account b;
  lookup(a); // calls lookup(const Account&)
  lookup(b); // calls lookup(Account&)

如果形参是普通的引用,则不能将 const 对象传递给这个形参。如果传递了 const 对象,则只有带 const 引用形参的版本才是该调用的可行函数。

如果传递的是非 const 对象,则上述任意一种函数皆可行。非 const 对象既可用于初始化 const 引用,也可用于初始化非 const 引用。但是,将 const 引用初始化为非 const 对象,需通过转换来实现,而非 const 形参的初始化则是精确匹配。

对指针形参的相关处理如出一辙。可将 const 对象的地址值只传递给带有指向 const 对象的指针形参的函数。也可将指向非 const 对象的指针传递给函数的 const 或非 const 类型的指针形参。如果两个函数仅在指针形参时是否指向 const 对象上不同,则指向非 const 对象的指针形参对于指向非 const 对象的指针(实参)来说是更佳的匹配。重复强调,编译器可以判断:如果实参是 const 对象,则调用带有 const* 类型形参的函数;否则,如果实参不是 const 对象,将调用带有普通指针形参的函数。

注意不能基于指针本身是否为 const 来实现函数的重载:

  f(int *);
  f(int *const); // redeclaration

此时,const 用于修改指针本身,而不是修饰指针所指向的类型。在上述两种情况中,都复制了指针,指针本身是否为 const 并没有带来区别。正如前面第 7.8 节所提到的,当形参以副本传递时,不能基于形参是否为 const 来实现重载。





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值