In this example, classes Foo
and Bar
are provided from a library. My class Baz
inherits from both.
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. 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.
Can I make func
call the virtual do_stuff
without renaming things?
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. I think it's the same case with function overloads, once you overload one you can't use base class implementations without 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
:
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, i.e. 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
.
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. Instead, a single scope must be identified for the name.
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 . But the name occurs in two different bases, so name lookup fails to identify a 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. 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
.
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.
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. 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With