1

我通过编译以下代码在 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;
}

任何解释为什么它是模棱两可的?提前谢谢。

4

3 回答 3

8

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

add();

可以解释为add(1,1)add()。当你写的时候void add(int a=1, int b=1),你是在告诉编译器—— “听着,如果我add不带参数调用,我希望你把它们默认为1

最重要的是,当您不带参数调用时,期望发生什么?add()

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

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

于 2012-06-03T00:05:37.710 回答
2
void add(int a, int b);
void add();

你不应该给出a默认b值。使用默认值,编译器无法知道调用是否add()应该使用第一个或第二个函数。

您是否有理由需要提供a默认b值?

于 2012-06-03T00:11:57.230 回答
2

重载解决方案由 C++ 标准(至少C++03C++11)的第 13.3 节定义。分为三个部分:

  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 条:

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

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

可行的功能

§ 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 的相应参数。如果参数具有引用类型,则隐式转换序列包括绑定引用的操作,以及对非 const 的左值引用不能绑定到右值以及右值引用不能绑定到左值的事实会影响函数的可行性(参见 13.3.3.1.4)。

参数列表有 0 个参数。add()有 0 个参数,所以它是可行的。add(int, int)有 2 个参数,但第一个有一个默认参数,所以它是可行的。由于调用中没有参数,因此第 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 确定最终结果:

如果恰好有一个可行函数比所有其他可行函数更好,那么它就是重载决议选择的那个;否则调用格式不正确。

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

于 2012-06-03T01:55:32.727 回答