多重继承导致伪模糊虚函数重载
Multiple inheritence leads to spurious ambiguous virtual function overload
在此示例中,classes Foo
和 Bar
由图书馆提供。我的 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 ();
}
我收到编译错误 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
.
的基础 class 实现
其实这和虚函数无关。下面的例子同样报错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
}
};
可能相关question
名称查找和重载解析不同。必须首先在作用域中找到该名称,即我们必须找到一个 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
,因此可以查找基础 classes。但是名称出现在两个不同的基础上,因此名称查找无法识别范围。我们永远无法解决重载问题。
另一个答案中建议的修复是有效的,因为它将名称 do_stuff
引入了 Baz
的范围,并且还为该名称引入了 2 个重载。因此名称查找确定 do_stuff
表示 Baz::do_stuff
,然后重载解析从称为 Baz::do_stuff
.
的两个函数中进行选择
顺便说一句,shadowing 是名称查找的另一个结果(本身不是规则)。名称查找选择内部范围,因此外部范围内的任何内容都不匹配。
当 参数相关查找 正在运行时,会出现一个更复杂的因素。简而言之,对于带有 class 类型参数的函数调用,会多次执行名称查找——我的回答中描述的基本版本,然后针对每个参数的类型再次进行名称查找。然后找到的作用域的并集进入重载集。但这不适用于您的示例,因为您的函数只有内置类型的参数。
在此示例中,classes Foo
和 Bar
由图书馆提供。我的 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 ();
}
我收到编译错误 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
}
};
可能相关question
名称查找和重载解析不同。必须首先在作用域中找到该名称,即我们必须找到一个 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
,因此可以查找基础 classes。但是名称出现在两个不同的基础上,因此名称查找无法识别范围。我们永远无法解决重载问题。
另一个答案中建议的修复是有效的,因为它将名称 do_stuff
引入了 Baz
的范围,并且还为该名称引入了 2 个重载。因此名称查找确定 do_stuff
表示 Baz::do_stuff
,然后重载解析从称为 Baz::do_stuff
.
顺便说一句,shadowing 是名称查找的另一个结果(本身不是规则)。名称查找选择内部范围,因此外部范围内的任何内容都不匹配。
当 参数相关查找 正在运行时,会出现一个更复杂的因素。简而言之,对于带有 class 类型参数的函数调用,会多次执行名称查找——我的回答中描述的基本版本,然后针对每个参数的类型再次进行名称查找。然后找到的作用域的并集进入重载集。但这不适用于您的示例,因为您的函数只有内置类型的参数。