关于c++函数重载的困惑

A confusion about c++ function overloading

本文关键字:重载 c++ 函数 关于      更新时间:2023-10-16

我通过编译以下代码来尝试c++中的默认参数值和函数重载,结果让我感到惊讶:

Line 19: error: call of overloaded 'add()' is ambiguous

我编译的代码是:

#include <iostream>
using namespace std;
void add(int a=1, int b=1){
cout<<a+b;
}
void add(){
int a =2, b=2;
cout<<a+b;
}

int main(){
add();
return 0;
}

有什么解释为什么它模棱两可吗?提前Thx。

因为两个签名都与调用匹配。

add();

可以被解释为CCD_ 1或CCD_。当你编写void add(int a=1, int b=1)时,你告诉编译器-"听着,伙计,如果我在没有参数的情况下调用add,我希望你将它们默认为1">

最重要的是,当您在没有参数的情况下调用add()时,YOU期望发生什么?

  • 如果希望打印2,请删除不带参数的版本。

  • 如果希望打印4,请从第一个版本中删除默认参数。

void add(int a, int b);
void add();

不应为aadd(1,1)0指定默认值。对于默认值,编译器无法知道对add()的调用应该使用第一个函数还是第二个函数。

您是否需要为ab提供默认值?

过载分辨率由C++标准的§13.3定义(至少是C++03和C++11)。有三个部分:

  1. 确定候选函数
  2. 从候选函数中确定可行函数
  3. 选择最佳可行函数

候选函数

由于add命名了一个函数(而不是对象),§13.3.1.1.1定义了如何确定候选函数。由于add不合格(不包含.->运算符),第3条适用(取自C++11的草案n3337):

在非限定函数调用中,名称不是由->或限定的。运算符,并具有更通用的主表达式形式。根据函数调用中名称查找的正常规则(3.4),在函数调用的上下文中查找名称。通过查找找到的函数声明构成候选函数集。由于名称查找的规则,候选函数集由(1)完全由非成员函数组成,或(2)完全由某个类T的成员函数组成。在情况(1)中,参数列表与调用中的表达式列表相同。[…]

简而言之,候选函数是在函数调用的上下文中通过标准名称查找找到的函数。名称查找在§3.4中有定义。通常,§3.4.2(依赖于参数的名称查找)会找到其他候选函数,但有问题的函数调用中没有参数,因此只有§3.4.1。特别是,第6条:

在函数的声明符id之后的函数定义中使用的名称是命名空间N的成员(其中,仅为了说明的目的,N可以表示全局范围),应在其在其中使用的块或其一个封闭块(6.3)中使用之前声明,如果N是嵌套的名称空间,则应在其在N的一个封闭名称空间中使用之前声明。

简而言之,将搜索当前命名空间和任何父命名空间,并且只考虑已声明的函数。在示例代码中,在名称为add的全局命名空间中,在main之前声明的任何函数都是候选函数:add()0和add()。如果在main之后声明(例如)函数add(float, float),它就不是候选函数。

可行函数

§13.3.2:

2首先,要成为一个可行的函数,候选函数应该有足够的参数,以便在数量上与列表中的参数一致。
  • 如果列表中有m个参数,则所有具有恰好m个参数的候选函数都是可行的
  • 参数少于m的候选函数只有在其参数列表中有省略号时才可行(8.3.5)。为了解决过载问题,任何没有相应参数的参数都被视为"匹配省略号"(13.3.3.1.3)
  • 只有当(m+1)-st参数具有默认参数(8.3.6)时,具有m个以上参数的候选函数才是可行的。为了解决过载问题,参数列表在右侧被截断,因此正好有m个参数
3其次,对于F是一个可行函数,每个参数都应存在一个隐式转换序列(13.3.3.1),该序列将该参数转换为F的相应参数。如果该参数具有引用类型,则隐式转换顺序包括绑定引用的操作,对非常量的左值引用不能绑定到右值,右值引用不能链接到左值,这可能会影响函数的可行性(见13.3.3.1.4)。

参数列表有0个参数。add()有0个参数,因此它是可行的。add(int, int)有两个参数,但第一个有一个默认参数,所以它是可行的。由于调用中没有参数,所以子句3中的转换不会起作用,但了解该子句很重要,尤其是当它指出声明为int foo(int&)的函数不能绑定到函数调用foo(0)时,因为非常量引用(例如int&)不能绑定到右值(例如文字0)。然而,int foo(const int&)可以与foo(0)结合。

最佳功能

§13.3.3定义了在函数偏序的名称解析方面,一个函数如何被认为比另一个"更好":

  1. 有些转换比其他转换"更好"(§13.3.3.2),使用更好参数转换的函数是更好的函数
  2. 如果1不能确定更好的函数,那么非模板函数比模板函数更好
  3. 如果2不能确定更好的函数,那么比另一个更专业化的函数模板专业化更好("更专业化"是§14.5.6.2中定义的偏序)

由于没有参数,因此不能使用条件1。add()add(int,int)都不是模板,因此2和3都不能使用。简而言之,两种功能都不如另一种。

最后,§13.3.3 2确定了最终结果:

如果恰好有一个可行函数比所有其他可行函数都好,那么它就是通过过载解析选择的函数;否则该调用是格式错误的

由于示例代码中有两个可行的函数,因此调用格式不正确。