关于功能:C ++重载决议

关于功能:C ++重载决议

C++ overload resolution

本问题已经有最佳答案,请猛点这里访问。

鉴于以下示例,为什么我必须显式使用语句b->A::DoSomething()而不仅仅是b->DoSomething()

编译器的重载决议不应该弄清楚我在谈论哪种方法?

我正在使用Microsoft VS 2005.(注意:在这种情况下使用虚拟无效。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}

两个"重载"不在同一范围内。默认情况下,编译器仅在找到名称匹配项之前才考虑可能的最小名称范围。之后进行参数匹配。在您的情况下,这意味着编译器看到B::DoSomething。然后它尝试匹配参数列表,该列表失败。

一种解决方案是将重载从A下拉到B的范围:

1
2
3
4
5
class B : public A {
public:
    using A::DoSomething;
    // …
}

重载分辨率是C ++中最丑陋的部分之一

基本上,编译器在B的范围内找到名称匹配"DoSomething(int)",看到参数不匹配,并且因错误而停止。

可以通过使用B类中的A :: DoSomething来克服它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;}
};  


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}


派生类中方法的存在会隐藏基类中具有相同名称(无论参数)的所有方法。这样做是为了避免这样的问题:

1
2
3
4
5
6
7
8
class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

比以后有人改变了A类:

1
2
3
4
class A
{
    void DoSomething(int ) {...}
}

现在突然:

1
2
B b;
b.DoSomething(1);     // calls A::DoSomething(1);

换句话说,如果它不能像这样工作,那么你不能控制的类(A)中的无关变化可能会默默地影响代码的工作方式。


不,这种行为是存在的,以确保您不会被错误地从远程基类继承。

要绕过它,您需要通过在B类中放置一个使用A :: DoSomething来告诉编译器您要调用哪个方法。

有关此行为的快速简单概述,请参阅此文章。


这与名称解析的工作方式有关。基本上,我们首先找到名称来自的范围,然后我们收集该范围内该名称的所有重载。但是,您的案例范围是B类,而在B类中,B :: DoSomething隐藏A :: DOSomething:

3.3.7名称隐藏[basic.scope.hiding]

... [剪断] ...

3在成员函数定义中,隐藏本地名称的声明
同一名成员的声明;看到
basic.scope.class。派生类中成员的声明
(class.derived)隐藏基类成员的声明
同名;见class.member.lookup。

由于名称隐藏,A :: DoSomething甚至不考虑重载解析


那不是超载!那是在隐藏!


在派生类中定义函数时,它会在基类中隐藏具有该名称的所有函数。如果基类函数是虚函数并且具有兼容签名,则派生类函数也会覆盖基类函数。但是,这不会影响可见性。

您可以使用using声明使基类函数可见:

1
2
3
4
5
6
class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};

该函数由子类中具有相同名称的函数隐藏(但具有不同的签名)。您可以使用using语句取消隐藏它,就像使用A :: DoSomething();


在向继承树中搜索要使用的函数时,C ++使用不带参数的名称,一旦找到它停止的任何定义,然后检查参数。在给出的示例中,它在B类中停止。为了能够完成您的工作,B类应该像这样定义:

1
2
3
4
5
6
class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
};

推荐阅读