为啥具有相同名称但不同签名的多个继承函数不会被视为重载函数?

Posted

技术标签:

【中文标题】为啥具有相同名称但不同签名的多个继承函数不会被视为重载函数?【英文标题】:Why do multiple-inherited functions with same name but different signatures not get treated as overloaded functions?为什么具有相同名称但不同签名的多个继承函数不会被视为重载函数? 【发布时间】:2011-07-19 03:20:04 【问题描述】:

以下 sn-p 在编译期间会产生“对 foo 的模糊调用”错误,我想知道是否有任何方法可以在不完全限定对 foo 的调用的情况下解决此问题:

#include <iostream>

struct Base1
    void foo(int)
    
;

struct Base2
    void foo(float)
    
;

struct Derived : public Base1, public Base2
;

int main()
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;

所以,问题正如标题所说。想法?我的意思是,以下工作完美无缺:

#include <iostream>

struct Base
    void foo(int)
    
;

struct Derived : public Base
    void foo(float)
    
;

int main()
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;

【问题讨论】:

将两个foo(第二种情况)中的日志语句添加到调用哪个函数的常量中,你会感到惊讶……C++充满了神秘的规则;) @Matthieu:喘气!该死的隐藏规则。 :( 【参考方案1】:

成员查找规则在第 10.2/2 节中定义

以下步骤定义在类范围C 中的名称查找结果。首先,考虑类及其每个基类子对象中名称的每个声明。如果AB 的基类子对象,则一个子对象B 中的成员名称f 隐藏子对象A 中的成员名称f任何如此隐藏的声明都将不予考虑。由 using-declaration 引入的这些声明中的每一个都被认为来自 C 的每个子对象,该子对象属于包含 using-declaration 指定的声明的类型。 如果声明的结果集并非全部来自同一类型的子对象,或者该集具有非静态成员并包含来自不同子对象的成员,则存在歧义并且程序格式错误强>。否则,该集合就是查找的结果。

class A 
public:
  int f(int);

;
class B 
public:
   int f();

;
class C : public A, public B ;
int main()

     C c;
     c.f(); // ambiguous

因此,您可以使用 using 声明 A::fB::f 来解决这种歧义

class C : public A, public B 
     using A::f;
     using B::f;

;

int main()

     C c;
     c.f(); // fine

第二个代码完美无缺,因为void foo(float) 在 C 的范围内。实际上d.foo(5); 调用void foo(float) 而不是int 版本。

【讨论】:

被调用的void foo(float) 版本真的让我到了那里.. 感谢您的广泛回答。 :) 想到的一件事......是否有一种情况,如果基类函数具有不同的签名,他们想要隐藏它们?当然,对于相同的签名函数,这很有用,但对于不同的签名函数,我无法想象一个好的例子...... 如果声明的结果集是..包含来自不同子对象的成员,则存在歧义并且程序格式错误此规则的原因是什么?否则会发生什么的例子会很好。【参考方案2】:

名称查找重载解析的独立阶段。

首先进行名称查找。这是决定名称适用于哪个范围的过程。在这种情况下,我们必须确定d.foo 是指d.D::foo,还是d.B1::foo,还是d.B2::foo。名称查找规则不考虑函数参数或任何东西;它纯粹是关于名称和范围。

只有在做出决定后,我们才会在找到名称的范围内对函数的不同重载执行重载解析。

在您的示例中,如果有这样的函数,调用 d.foo() 会找到 D::foo()。但是没有。因此,向后工作范围,它尝试基类。现在foo 可以同样查找B1::fooB2::foo,所以它是模棱两可的。

出于同样的原因,在 D 成员函数中调用不合格的 foo(5); 会产生歧义。


推荐方案的效果:

struct Derived : public Base1, public Base2
    using Base1::foo;
    using Base2::foo;

这会创建名称D::foo,并使其识别两个函数。结果是d.foo解析为d.D::foo,然后重载解析可以发生在D::foo标识的这两个函数上。

注意:在本例中,D::foo(int)Base1::foo(int) 是一个函数的两个标识符;但总的来说,对于名称查找和重载解析过程,它们是否是两个独立的函数并没有区别。

【讨论】:

【参考方案3】:

它对你有用吗?

struct Derived : public Base1, public Base2
   using Base2::foo;

【讨论】:

以上是关于为啥具有相同名称但不同签名的多个继承函数不会被视为重载函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在构造函数内部声明和初始化的变量已经在外部以相同的名称声明时被视为不同的变量?

为啥继承具有名称签名的接口成员的 C# 抽象类至少需要实现其中一个?

四. Java继承和多态3. 继承中的方法的覆盖和重载

C#中具有相同名称和签名但返回类型不同的方法

C#中具有相同名称和签名但返回类型不同的方法

创建具有相同名称但不同签名的别名