Fuzzy warning about overridden virtual method

I have a Base class that provides some business logic and virtual methods that you can override. In addition, I want to extend some classes that inherit from Base with Decorator . Here's a 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 the 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 does not complain and, frankly, I do not know what clang finds wrong here.

I am running recent clang and GCC using Compiler Explorer: https://godbolt.org/g/fC5XXT

+5
source share
3 answers

Edit to include AnT comments:

Clang behaves correctly following the C ++ name hiding rule. A brief description is available here . In short ...

A member of a derived class hides any member of the base class that has the same name as the member of the derived class.

This includes the base class methods marked virtual .

Original answer:

It seems that Klang gives priority to the name of the function, rather than the signature when choosing the method you are trying to call. Here is an example of using 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); } 

Note the additional warning generated on 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 if the Middle class inherits the void foo(double) method from Base , Clang seems to assume that you intended to call the void foo(int) override method declared in Middle .

As already mentioned, you can add more overrides to help Clang resolve the method you intended to call. Another solution is provided by fooobar.com/questions/122130 / ... with the using keyword. Announcements in Middle and Final will 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

+1
source

This is a warning about hiding the name. If you declare a name (overriding foo ) in scope, it hides all declarations of this namme in external objects (in this case, base classes). Decorator doesn't matter 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 behavior of the m and f calls is surprising to many, so Clang gives 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 this case, all classes will have foo(int) and foo(double)

+3
source

The initial intention of this warning -Woverloaded-virtual was to catch the following situations (in an era when override did not exist yet)

 struct Base { virtual void foo(double) {} }; struct Middle : Base { void foo(int) {} }; 

It generates the same warning. The compiler suspects that you mistakenly specified a list of parameters in the declaration of the function of the derived class.

What you observe in your case is probably the unintended side effect of this warning. In modern C ++ override , it is guaranteed that the parameter list is correct and intentional, but the compiler is still not convinced: the presence of a "hidden" overloaded version of these functions still raises a warning.

As you yourself noted, GCC is more intelligent in dealing with this situation. Obviously in GCC, -Woverloaded-virtual not part of -Wall -Wextra - you must explicitly specify it.

0
source

Source: https://habr.com/ru/post/1273247/


All Articles