I have a Base
class that provide some business logic and virtual methods, that can be further overridden. Additionally I want to extend some classes inheriting from Base
with Decorator
. Here's simplified setup:
struct Base
{
~Base() = default;
virtual void foo(int) {};
virtual void foo(double) {};
};
template<typename T>
struct Decorator : public T
{
};
struct Middle : public Decorator<Base>
{
virtual void foo(int) override {};
};
struct Final : public Middle
{
virtual void foo(double) override {};
};
When I compile code with clang and -Wall -Wextra I get following warning:
21 : <source>:21:18: warning: 'Final::foo' hides overloaded virtual function [-Woverloaded-virtual]
virtual void foo(double) override {};
^
16 : <source>:16:18: note: hidden overloaded virtual function 'Middle::foo' declared here: type mismatch at 1st parameter ('int' vs 'double')
virtual void foo(int) override {};
^
GCC doesn't complain and to be honest I don't know what clang finds wrong here.
I run recent recent clang and GCC using Compiler Explorer: https://godbolt.org/g/fC5XXT
This warning is all about name hiding. If you declare a name (an override of foo
) in a scope, it hide all declarations of that namme in "outer" scopes (in this case, base classes). Decorator
is an irrelevance here.
struct Base
{
~Base() = default;
virtual void foo(int) {};
virtual void foo(double) {};
};
struct Middle : public Base
{
void foo(int) override {};
};
struct Final : public Middle
{
void foo(double) override {};
};
int main()
{
Final f;
f.foo(0.0); // Calls Final::foo(double);
f.foo(0); // *Also* calls Final::foo(double) - because Middle::foo(int) is hidden.
Middle& m = f;
m.foo(0); // Calls Middle::foo(int);
m.foo(0.0); // *Also* calls Middle::foo(int) - because Base::foo(double) is hidden.
Base& b = m;
b.foo(0); // Calls Middle::foo(int) - because that overrides Base::foo(int) and
// the dynamic type of b is a (sub-class of) Middle.
b.foo(0.0); // Calls Final::foo(double) - because that override Base::foo(double) and
// the dynamic type of b is Final.
return 0;
}
The behaviour of the calls to m and f are surprising to many, so Clang issues a warning. You can suppress it with:
struct Middle : public Base
{
using Base::foo;
void foo(int) override {};
};
struct Final : public Middle
{
using Middle::foo;
void foo(double) override {};
};
In which case all classes will have foo(int)
and foo(double)
Clang is behaving correctly by following C++'s name hiding rule. A short and sweet description is available here. In brief...
A member of a derived class hides any member of a base class that has the same name as the derived class member.
This includes base class methods marked virtual
.
It looks like Clang is giving priority to the function name and not the signature when deciding which method you are trying to call. Here is an example usage of your classes...
int main(void) {
Final f;
f.foo(3.14159);
f.foo(0);
Middle* m = static_cast<Middle*>(&f);
m->foo(3.14159);
m->foo(0);
Base* b = static_cast<Base*>(&f);
b->foo(3.14159);
b->foo(0);
}
Notice the additional warning generated at the call site...
32 : <source>:32:12: warning: implicit conversion from 'double' to 'int' changes value from 3.14159 to 3 [-Wliteral-conversion]
m->foo(3.14159);
~~~ ^~~~~~~
https://godbolt.org/g/qkjqEN
Even though the Middle
class inherits the void foo(double)
method from Base
, Clang seems to be assuming you meant to call the void foo(int) override
method declared in Middle
.
As others have mentioned, you can add more overrides to help Clang resolve the method you intended to call. Another solution is provided by this StackOverflow question with the using
keyword. The declarations in Middle
and Final
would become the following...
struct Middle : public Decorator<Base>
{
using Base::foo;
void foo(int) override { std::cout << "Middle::foo" << std::endl; };
};
struct Final : public Middle
{
using Base::foo;
void foo(double) override { std::cout << "Final::foo" << std::endl; };
};
https://godbolt.org/g/Qe1WMm
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