I have been playing around with C 's inheritance, checking which methods are overridden and which get called in different situations. I ended up with the following code:
#include <iostream>
class EmptyA {};
class DerEmptyA : public EmptyA {};
class DerDerEmptyA : public DerEmptyA {};
class Base
{
public:
void f(EmptyA)
{
std::cout << "Base::f(EmptyA)\n";
}
void f(DerEmptyA)
{
std::cout << "Base::f(DerEmptyA)\n";
}
virtual void handle(EmptyA ea)
{
std::cout << "Base::handle(EmptyA) - ";
f(ea);
}
};
class Derived : public Base
{
public:
void f(DerDerEmptyA)
{
std::cout << "Derived::f(DerDerEmptyA)\n";
}
virtual void handle(EmptyA ea) override final
{
std::cout << "Derived::handle(EmptyA) - ";
f(ea);
}
};
int main()
{
Derived d;
d.handle(EmptyA{});
d.handle(DerDerEmptyA{});
}
I expected the first call to handle
from main (that is d.handle(EmptyA{})
) to be forwarded to Base::f(EmptyA)
, however, on my g (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
this results in a compilation error:
tmp.cpp: In member function ‘virtual void Derived::handle(EmptyA)’:
tmp.cpp:39:5: error: cannot convert ‘EmptyA’ to ‘DerDerEmptyA’
39 | f(ea);
| ^~
| |
| EmptyA
tmp.cpp:31:9: note: initializing argument 1 of ‘void Derived::f(DerDerEmptyA)’
31 | void f(DerDerEmptyA)
| ^~~~~~~~~~~~
Note: replacing the line f(ea);
with Base::f(ea);
in the Derived::handle
function does not seem like a correct solution because in that case the next call from main (that is d.handle(DerDerEmptyA{})
) will be forwarded to Base::f(EmptyA)
instead of Derived::f(DerDerEmptyA)
.
So, I have two questions:
- Why did
Base::f(EmptyA)
become inaccessible fromDerived::handle
? - How can I fix this code so that every call to
Derived::handle
is forwarded to the most specific version of methodf
?
CodePudding user response:
To solve this (that is, if you want the Base version of f
), you can use:
Base::f(ea);
For calling/using the Base
version.
The problem is when you use just f(ea);
the Derived version of f
is selected since the search starts from Derived
class and stops when it founds f
.
The program works if you use Base::f(ea);
as can be seen here.
Edit
Now seeing your edited question and the comment below, i think you have missed one thing which is
the
handle()
function inside the Derived class takes parameter of typeEmptyA
only and there is no version of handle() inside the Derived class that takes parameter of typeDerDerEmptyA
. So both of your calls to handle() will call the sameBase::f
that takesEmptyA
as argument.
If you want the specific version called then you have to provide one as i did in my modified example below which have your desired output/result:
#include <iostream>
class EmptyA {};
class DerEmptyA : public EmptyA {};
class DerDerEmptyA : public DerEmptyA {};
class Base
{
public:
void f(EmptyA)
{
std::cout << "Base::f(EmptyA)\n";
}
void f(DerEmptyA)
{
std::cout << "Base::f(DerEmptyA)\n";
}
//added this
void f(DerDerEmptyA)
{
std::cout << "Base::f(DerDerEmptyA)\n";
}
virtual void handle(EmptyA ea)
{
std::cout << "Base::handle(EmptyA) - ";
f(ea);
}
//added this
virtual void handle(DerDerEmptyA ea)
{
std::cout << "Base::handle(DerDerEmptyA) - ";
f(ea);
}
};
class Derived : public Base
{
public:
void f(DerDerEmptyA)
{
std::cout << "Derived::f(DerDerEmptyA)\n";
}
virtual void handle(EmptyA ea) override final
{
std::cout << "Derived::handle(EmptyA) - ";
Base::f(ea);
}
//added this
virtual void handle(DerDerEmptyA ea) override final
{
std::cout << "Derived::handle(DerDerEmptyA) - ";
Base::f(ea);
}
};
int main()
{
Derived d;
d.handle(EmptyA{});
d.handle(DerDerEmptyA{});
}
Also take notice of the implicit conversions that may be happening(if any).
CodePudding user response:
Bring Base::f
into Derived
's scope:
class Derived : public Base
{
public:
using Base::f;
//...
};