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

时间:2014-09-17 09:55:46

标签: c++ c++11

我有下一个代码:

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甚至将它放在错误消息中!)。

请不要将此标记为重复 - 这是与Calling in C++ a non member function inside a class with a method with the same

不同的问题

4 个答案:

答案 0 :(得分:21)

  

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

因为那些是规则。嵌套范围中的名称会在更广泛的范围内隐藏具有相同名称的实体。

  

这项要求的动机是什么?

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

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

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

void f(int);

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

正如您(以及您的有用编译器)所说,如果您需要,外部名称仍然可用(具有资格)。

答案 1 :(得分:8)

编译器为f执行非限定名称查找,在§3.4.1[basic.lookup.unqual]中指定(幸运的是,这里没有ADL):

  

1在3.4.1中列出的所有情况下,搜索范围为a   按各自类别列出的顺序进行声明;   一旦找到名称的声明,名称查找就会结束。如果不   声明被发现,该程序是不正确的。

     

8对于类X的成员,在成员函数中使用的名称   body,在默认参数中,在 exception-specification 中,在    brace-or-equal-initializer 非静态数据成员(9.2),或者在X定义之外的类成员的定义中,   在成员的 declarator-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(),它就会停止,从不在其他地方搜索。

在重载解析后,在名称查找后拒绝拒绝不可行的函数。

答案 2 :(得分:1)

  

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

这就是拥有命名空间的重点。本地(更近距离)的名称是首选,并且在全局名称上更明显。由于struct又是范围,因此f会影响::f的可见性。当你拥有全球性的时候,你可以说你做到了。为什么呢?

这是作为一项功能提供的,以确保您可以安静地调用您定义的函数,假设它们将被调用,并且当您需要来自不同命名空间的函数(例如标准库)时,您明确说明,例如std::。它只是一种清晰的消除歧义形式,没有机会发挥其作用。

答案 3 :(得分:0)

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

编译器首先做的是名称查找

非限定名称查找从当前范围开始,然后向外移动;它会在找到函数名称的声明后立即停止,即使此函数稍后将被确定为不是函数调用的可行候选者。

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

(最后,访问检查会在超载分辨率获取的函数上执行。)