为什么调用与成员函数同名的非成员函数会产生错误

Why calling a non-member function with the same name as a member function generates an error

本文关键字:函数 成员 错误 调用 为什么      更新时间:2023-10-16

我有下一个代码:

void f(int){}
struct A
{
    void f()
    {
        f(1);
    }
};

此代码的格式不正确,错误消息(GCC):error: no matching function for call to ‘A::f(int)’或(clang)Too many arguments to function call, expected 0, have 1; did you mean '::f'?

为什么我需要使用::来调用与成员函数同名但签名不同的非成员函数?这个要求的动机是什么

我认为编译器应该能够弄清楚我想调用非成员函数,因为签名不同(clang甚至把它放在错误消息中!)。

请不要将其标记为重复-这与在C++中调用具有相同的方法的类内的非成员函数是不同的问题

为什么我需要使用::来调用与成员函数同名但签名不同的非成员函数

因为这些都是规则。嵌套作用域中的名称会在更大的作用域中隐藏具有相同名称的实体。

这个要求的动机是什么?

考虑一个成员函数调用另一个签名不完全匹配的成员的情况:

struct A {
    void f(double);
    void g() {f(42);}  // requires int->double conversion
};

现在假设有人在周围的名称空间中添加了一个不相关的函数

void f(int);

如果这包含在A范围内的一组重载中,那么A::g的行为会突然发生变化:它会调用它而不是A::f。将重载集限制为最窄可用范围内的名称可以防止这种意外的破坏。

正如你(和你乐于助人的编译器)所说,如果你需要,外部名称仍然可用(有资格)。

编译器对§3.4.1[basic.lookup.uqual]中指定的f执行无资格名称查找(谢天谢地,这里没有ADL):

1在3.4.1.中列出的所有情况下,都会在范围中搜索按各个类别中列出的顺序进行申报;一旦找到名称的声明,名称查找就会结束。如果没有找到声明,程序格式错误。

8对于类X的成员,在成员函数中使用的名称body,在默认参数中,在异常规范中,在非静态数据成员(9.2)的brace或equal初始值设定项,或者在X的定义之外的类成员的定义中,在成员的声明符id之后,应在以下其中之一中声明以下方式:

  • 在其使用的砌块或封闭砌块(6.3)中使用之前,或
  • 应为X类成员或X(10.2)基类成员,或
  • 如果X是类Y(9.7)的嵌套类,则应是Y的成员,或者应是Y基类的成员(此查找适用依次转到Y的封闭类,从最内层开始封闭类),或者
  • 如果X是局部类(9.8)或是局部类的嵌套类,则在包含定义的块中的类X的定义之前X类,或
  • 如果X是命名空间N的成员,或者是属于N成员的类的嵌套类,或者是本地类或中的嵌套类在使用之前,属于N成员的函数的局部类名称,位于名称空间NN的封闭名称空间之一中

一旦找到声明,名称查找就会停止。因此,一旦它在第二个项目符号处找到成员f(),它就会停止并从不在其他地方搜索。

拒绝不可行的函数是在名称查找后,以过载分辨率完成的。

为什么我需要使用::来调用与成员函数同名但签名不同的非成员函数?这个要求的动机是什么?

这就是拥有名称空间的全部意义。与全局名称相比,本地(范围更近)名称更受欢迎,也更可见。由于struct又是一个作用域,因此它的f遮蔽了::f的可见性。当你必须拥有全球性的时,你必须说你拥有。为什么?

这是作为一个功能提供的,以确保您可以和平地调用您定义的函数,假设它们会被调用,并且当您需要来自不同命名空间(比如标准库)的函数时,您可以明确声明,比如std::。这只是一种纯粹的歧义消除形式,没有留下发挥作用的机会。

要了解错误的原因以及为什么需要显式使用::f()语法,您可能需要考虑C++编译器过程的某些方面:

编译器所做的第一件事是名称查找

不合格名称查找从当前范围开始,然后向外移动;一旦找到函数名称的声明,它就会停止,即使该函数稍后被确定为不是函数调用的可行候选函数。

然后,对名称查找找到的函数集执行重载解析

(最后,访问检查是在过载解析获得的函数上执行的。)