派生类中有相同名称但不同签名的函数找不到

101

我在一个基类和派生类中有同名但不同签名的函数。当我试图在继承自派生类的另一个类中使用基类的函数时,会收到错误提示。请查看以下代码:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

我从gcc编译器收到以下错误信息:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

如果我从类B中移除int foo(int i){};或者将其重命名为foo1,一切都能正常工作。

这有什么问题吗?


1
虽然技术上是 这个问题 的重复,但这个问题有更好的标题和答案。 - Troubadour
2个回答

115

这是因为名称查找会在找到任一基类中的名称后停止,不会查找其他基类。B中的函数遮蔽了A中的函数。您需要在B的作用域内重新声明A的函数,以便B和C都可以从中看到这两个函数:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

编辑:标准真正给出的描述是(来自10.2/2):

  

以下步骤定义了类作用域C中名称查找的结果。首先,考虑类及其每个基类子对象中名称的每个声明。如果一个子对象B中的成员名称f隐藏了子对象A中的成员名称f,则称为A是B的一个基类子对象。任何被隐藏的声明都会被从考虑中消除。将由using-declaration引入的每个这些声明都视为来自于包含由using-declaration指定的声明的C的每个子对象的类型。96) 如果所得到的声明集不全来自于相同类型的子对象,或该集合具有非静态成员并且包括来自于不同子对象的成员,则存在歧义,并且程序无效。否则,该集合是查找的结果。

在另一个地方它还有以下说明(就在上面):

  

对于id-expression [类似于“foo”],名称查找始于此的类作用域; 对于限定id [类似于“A :: foo”,其中A是嵌套名称说明符],名称查找始于嵌套名称说明符的范围内。名称查找在访问控制之前进行(3.4,第11条)。

([...]由我加上)。请注意,这意味着即使B中的foo是private的,仍然找不到A中的foo(因为访问控制发生得更晚)。


litb,谢谢你的回答。但是当我尝试编译你的代码时,我得到了以下错误信息:无法调整对 class B' 中的 void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' 的访问权限,因为存在同名的局部方法 `int B::foo(int)'。可能是因为我使用的是旧版本的gcc。 - Igor
1
是的,肯定是编译器的错误。旧的编译器使用"A::foo;"而不是"using A::foo;",但前者在C++中已被弃用。 - Johannes Schaub - litb

88

派生类中的函数如果名称与基类中的函数相同但不重写基类函数,则会隐藏基类中同名的其他函数。

通常认为,在派生类中拥有与基类中同名但没有意图覆盖基类函数的函数是不良实践,因为这种行为通常是不可取的。通常最好为不同的函数命名不同的名称。

如果需要调用基函数,您需要使用A::foo(s)来限定调用范围。请注意,同时这也会禁用A::foo(string)的任何虚函数机制。


14
请参考litdb的回答:你可以在B中使用"using A::foo"从而“取消隐藏”基类函数。 - xtofl
没错,我只是在看一个可以在调用站点使用的解决方案,将基本层次结构视为固定。 - CB Bailey
2
这个声明的基础是什么,接着给出建议:“通常认为在派生类中有与基类中函数同名的函数是不好的实践,除非它们是用来覆盖基类函数的。因为你所看到的行为通常是不可取的。通常最好给不同的函数命名不同的名称。”但如果它们在语义上做着相同的事情呢?然而,C++提供了解决这个问题的方法,正如Johannes的答案所解释的那样。 - Nawaz
派生类中的函数如果没有覆盖基类中的函数,但是名称相同,则会隐藏基类中同名的其他函数。如果名称和签名都相同呢?我有点困惑,因为我找到了几篇文章认为这是C++版本的方法重写(例如g4g C++中的函数重写)。 - MTV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接