在本例中,类Foo
和Bar
是从库中提供的。我的类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 ();
}
我得到了编译错误reference to ‘do_stuff’ is ambiguous
,这对我来说似乎是伪造的,因为两个函数签名完全不同。如果do_stuff
是非虚拟的,我可以调用Bar::do_stuff
来消除它的歧义,但这样做会破坏多态性并导致链接器错误。
我可以让func
调用虚拟do_stuff
而不重命名东西吗?
您可以这样做:
struct Baz : public Foo, public Bar
{
using Bar::do_stuff;
using Foo::do_stuff;
//...
}
使用wandbox-gcc最新版本进行测试,编译良好。我认为函数重载也是如此,一旦重载了一个函数,就不能使用没有using
的基类实现。
事实上,这与虚拟函数无关。以下示例具有相同的错误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
}
};
可能的相关问题
名称查找和重载解析不同。必须首先在作用域中找到名称,即我们必须找到一个X
,以便将名称do_stuff
解析为X::do_stuff
——与名称的使用无关——然后重载解析在X::do_stuff
的不同声明之间进行选择。
该过程不是识别所有可见的A::do_stuff
、B::do_stuff
等情况,然后在这些情况的并集之间执行过载解决。相反,必须为名称标识单个作用域。
在此代码中:
struct Baz : public Foo, public Bar
{
void func ()
{
do_stuff (1.1f); // ERROR HERE
}
};
Baz
不包含名称do_stuff
,因此可以查找基类。但是名称出现在两个不同的基中,所以名称查找无法识别作用域。我们从未达到超负荷解决的程度。
另一个答案中的建议修复有效,因为它将名称do_stuff
引入了Baz
的范围,并且还为该名称引入了2个重载。因此,名称查找确定do_stuff
意味着Baz::do_stuff
,然后过载解析从已知为Baz::do_stuff
的两个函数中进行选择。
顺便说一句,阴影是名称查找的另一个结果(本身不是规则(。名称查找选择内部作用域,因此外部作用域中的任何内容都不匹配。
当参数相关查找正在进行时,会出现更复杂的因素。简单地说,对于具有类类型参数的函数调用(如我的回答中所述的基本版本(,会多次进行名称查找,然后对每个参数的类型再次进行名称查找。然后,找到的作用域的并集进入重载集。但这并不适用于您的示例,因为您的函数只有内置类型的参数。