2012-01-28 108 views
10
void foo(int) 
{ 
} 

class X 
{ 
    void foo() 
    { 
    } 

    void bar() 
    { 
     foo(42); 
     // error: no matching function for call to 'X::foo(int)' 
     // note: candidate is: 
     // note: void X::foo() 
     // note: candidate expects 0 arguments, 1 provided   
    } 
}; 

为什么C++无法调用自由函数(这是唯一具有正确签名的函数)?成员函数隐藏自由函数

+2

在这种情况下,您可以使用':: foo(42)'来访问外部foo。 [Ideone演示](http://ideone.com/6HljO)。但是我对命名空间并不了解太多。 – 2012-01-28 12:23:09

+0

我认为这是C++的一个致命弱点。 它使优雅地使用普通的重载自由函数名称变得不可能,例如isempty(thing),其中有许多重载的isempty给定类型的东西,同时允许thing.isempty()也存在。愚蠢,不幸,笨重。 – Mordachai 2014-02-07 20:01:29

回答

5

的合乎逻辑的理由是一致性

  • 假设按照建议,编译器将解析foo(42)::foo(int)
  • 现在过了某段时间,如果您将X::foo()更改为X::foo(int),则 foo(42)将解析为X::foo(int)。这不一致。

这也是派生类函数隐藏基类函数时有类似名称的原因。

这种情况可以通过2种方式解决;

(1)给出完全合格的名称(例如::foo(42)

(2)使用using效用;例如

void bar() 
{ 
    using ::foo; 
    foo(42); 
} 
+1

如果某人后来添加foo(int)成员,那么他们明确地打算这样做。糟糕的语言设计,IMO。 – Mordachai 2014-02-07 20:02:54

12

由于这两个标识符是在不同的作用域中定义的,而重载解析仅涉及同一作用域中的函数。一旦编译器发现该类有foo,它将停止向上扩展(C++ 11§3.4.1/ 1),因此隐藏了自由函数foo

你需要使用一个合格的名称来指代全球foo

::foo(42); 
+1

注意:这是一个特定的情况,因为'int',大部分时间它仍然工作,因为ADL是好的。 – 2012-01-28 13:19:07

0

我不能回答你的问题的原因一部分 - 我不知道什么是语言规范背后的基本原理。

要调用全局函数在你的榜样,使用::语法:

::foo(42); 
0

原因是编译器会先查找匹配的函数名,忽略返回值和参数。在类内部时,它会尝试在那里寻找一个匹配的成员(实际上,它将遍历所有向上“向上”的作用域;局部作用域,函数作用域,类作用域,命名空间作用域,全局作用域等等。 )。

X::foo是第一个匹配的名称。 THEN(不是之前)它会尝试根据参数选择正确的重载(如果有多个声明)(这就是你可以用不同的参数重载同一个函数但只返回不同的值的原因),然后它会检查返回值(如果有的话)。

1

真的很喜欢你的问题。此外,我可以说,使用此语法:

::foo(42); 

,但我可以说,在我看来,它更优雅和良好的编程,集命名空间,所以你可以写这样的事情:

namespace MyNameSpace 
{ 
    void foo(int){} 

    class X 
    { 
     void foo(){} 

     void bar() 
     { 
      MyNameSpace::foo(42); 
     } 
    }; 
}; 

这是一件好事,因为Namespaces允许在名称下对类,对象和函数进行分组。

PS:然后,这可以帮助您了解在没有任何命名空间时编写::foo(42);的含义。

2

内部作用域中的名称隐藏了外部作用域中的名称。不管它是一个函数还是别的东西,或者如果你在一个类或一个命名空间中。

只有当名称查找找到具有相同名称的多个函数时,重载解析才会启动以尝试选择最适合该调用的函数。