简体   繁体   English

多重继承导致虚假模糊虚拟 function 过载

[英]Multiple inheritence leads to spurious ambiguous virtual function overload

In this example, classes Foo and Bar are provided from a library.在此示例中,类FooBar是从库中提供的。 My class Baz inherits from both.我的 class Baz继承自两者。

struct Foo
{
    void do_stuff (int, int);
};

struct Bar
{
    virtual void do_stuff (float) = 0;
};

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

struct BazImpl : public Baz
{
    void do_stuff (float) override {};
};

int main ()
{
    BazImpl () .func ();
}

I get the compilation error reference to 'do_stuff' is ambiguous which seems spurious to me since the two function signatures are entirely different.我得到reference to 'do_stuff' is ambiguous ,这对我来说似乎是虚假的,因为两个 function 签名完全不同。 If do_stuff was non-virtual I could call Bar::do_stuff to disambiguate it, but to do so breaks polymorphism and causes a linker error.如果do_stuff是非虚拟的,我可以调用Bar::do_stuff来消除歧义,但这样做会破坏多态性并导致 linker 错误。

Can I make func call the virtual do_stuff without renaming things?我可以在不重命名的情况下让func调用虚拟do_stuff吗?

You can do this:你可以这样做:

struct Baz : public Foo, public Bar
{
    using Bar::do_stuff;
    using Foo::do_stuff;
    //...
}

Tested with wandbox gcc latest and it compiles fine.用最新的wandbox gcc 测试,编译良好。 I think it's the same case with function overloads, once you overload one you can't use base class implementations without using .我认为 function 重载的情况相同,一旦重载一个,你就不能使用基本 class 实现而不using .

In fact this has nothing to do with virtual functions.实际上这与虚函数无关。 The following example has the same error GCC 9.2.0 error: reference to 'do_stuff' is ambiguous :以下示例具有相同的错误GCC 9.2.0 error: reference to 'do_stuff' is ambiguous

struct Foo
{
    void do_stuff (int, int){}
};

struct Bar
{
    void do_stuff (float) {}
};

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

Possible related question可能的相关问题

Name lookup and overload resolution are different.名称查找和重载解析是不同的。 The name must be found in a scope first, ie we must find an X so that the name do_stuff is resolved to X::do_stuff -- independently of the usage of the name -- and then overload resolution selects between the different declarations of X::do_stuff .该名称必须首先在 scope 中找到,即我们必须找到一个X以便将名称do_stuff解析为X::do_stuff与名称的使用无关——然后重载解析在X::do_stuff的不同声明之间进行选择X::do_stuff

The process is NOT to identify all such cases A::do_stuff , B::do_stuff , etc. that are visible, and then perform overload resolution amongst the union of that.该过程不是识别所有可见A::do_stuffB::do_stuff等情况,然后在它们的联合中执行重载解决方案。 Instead, a single scope must be identified for the name.相反,必须为名称标识单个 scope。

In this code:在这段代码中:

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

Baz does not contain the name do_stuff , so base classes can be looked up. Baz不包含名称do_stuff ,因此可以查找基类。 But the name occurs in two different bases, so name lookup fails to identify a scope.但名称出现在两个不同的基数中,因此名称查找无法识别 scope。 We never get so far as overload resolution.我们从来没有达到过重载决议。

The suggested fix in the other answer works because it introduces the name do_stuff to the scope of Baz , and also introduces 2 overloads for the name.另一个答案中的建议修复有效,因为它将名称do_stuff引入了Baz的 scope ,并且还为该名称引入了 2 个重载。 So name lookup determines that do_stuff means Baz::do_stuff and then overload resolution selects from the two functions that are known as Baz::do_stuff .因此,名称查找确定do_stuff表示Baz::do_stuff ,然后重载决议从称为Baz::do_stuff的两个函数中进行选择。


As an aside, shadowing is another consequence of name lookup (not a rule in itself).顺便说一句,阴影是名称查找的另一个结果(本身不是规则)。 Name lookup selects the inner scope, and so anything in the outer scope is not a match.名称查找选择内部 scope,因此外部 scope 中的任何内容都不匹配。

A further complicating factor occurs when argument-dependent lookup is in play.依赖于参数的查找在起作用时,会出现更复杂的因素。 To summarize very briefly, name lookup is done multiple times for a function call with arguments of class type -- the basic version as described in my answer, and then again for each argument's type.简而言之,对于使用 class 类型的 arguments 的 function 调用(我的回答中描述的基本版本),名称查找会进行多次,然后再针对每个参数类型。 Then the union of the scopes found goes into the overload set.然后找到的范围的联合进入重载集。 But that does not apply to your example since your function only has parameters of built-in type.但这不适用于您的示例,因为您的 function 只有内置类型的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM