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

发布于 2024-10-25 06:20:55 字数 614 浏览 6 评论 0原文

以下代码片段在编译期间产生“对 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;
}

The following snippet produces an "ambigious call to foo" error during compilation, and I'd like to know if there is any way around this problem without fully qualifying the call to 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;
}

So, question is as the title says. Ideas? I mean, the following works flawlessly:

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风蛊 2024-11-01 06:20:55

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

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


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 版本。

Member lookup rules are defined in Section 10.2/2

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declara-tion designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

class A {
public:
  int f(int);

};
class B {
public:
   int f();

};
class C : public A, public B {};
int main()
{
     C c;
     c.f(); // ambiguous
}

So you can use the using declarations A::f and B::f to resolve that ambiguity

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

};

int main()
{
     C c;
     c.f(); // fine
}

The second code works flawlessly because void foo(float) is inside C's scope. Actually d.foo(5); calls void foo(float) and not the int version.

深居我梦 2024-11-01 06:20:55

名称查找重载解析的一个单独阶段。

首先进行名称查找。这是决定名称适用范围的过程。在这种情况下,我们必须决定 d.foo 是否表示 dD::food.B1::food .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 解析为 dD::foo,然后重载解析可能发生在由 D::foo< 标识的这两个函数上/代码>。

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

Name lookup is a separate phase to overload resolution.

Name lookup occurs first. That is the process of deciding which scope the name applies to. In this case we must decide whether d.foo means d.D::foo, or d.B1::foo, or d.B2::foo. The name lookup rules do not take into account function parameters or anything; it is purely about names and scopes.

Only once that decision has been made, do we then perform overload resolution on the different overloads of the function in the scope where the name was found.

In your example, calling d.foo() would find D::foo() if there were such a function. But there is none. So, working backwards up the scopes, it tries the base classes. Now foo could equally look up to B1::foo or B2::foo so it is ambiguous.

For the same reason, you would get ambiguity calling unqualified foo(5); inside a D member function.


The effect of the recommended solution:

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

is that this creates the name D::foo, and makes it identify two functions. The result is that d.foo resolves to d.D::foo, and then overload resolution can happen on these two functions that are identified by D::foo.

Note: In this example D::foo(int) and Base1::foo(int) are two identifiers for the one function; but in general, for the name lookup and overload resolution process, it doesn't make a difference whether they are two separate functions or not.

毁梦 2024-11-01 06:20:55

它对你有用吗?

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

Will it work for you?

struct Derived : public Base1, public Base2{
   using Base2::foo;}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文