1

I've been reading everywhere I can find and I just can't understand what the warning GCC is giving me, is upset about. I have this code:

class A;
class B;

class Upper
{
    virtual void foo(A& a);
};

class Lower : public Upper
{
    void foo(B& b);
};

and GCC gives me this error:

$ g++ -Wall -O2 -c -o /tmp/over.o /tmp/over.cpp
/tmp/over.cpp:6:18: warning: 'virtual void Upper::foo(A&)' was hidden [-Woverloaded-virtual=]
    6 |     virtual void foo(A& a);
      |                  ^~~
/tmp/over.cpp:11:10: note:   by 'void Lower::foo(B&)'
   11 |     void foo(B& b);
      |          ^~~

I just don't get it. Maybe someone needs to use small words. It's clear (to me) that these two methods are completely unambiguous and if I call foo(a) with an instance of class A I would call the virtual method and if I call foo(b) with an instance of class B I would call the non-virtual method. So, what's the problem here that the compiler is complaining about?

I've seen many questions where the overloaded methods could be ambiguous, such as argument of int vs. char or whatever, but here there's no possibility of confusion, which leaves me, well, confused!

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • 1
    Does this answer your question? [Function with same name but different signature in derived class not found](https://stackoverflow.com/questions/411103/function-with-same-name-but-different-signature-in-derived-class-not-found) – ChrisMM Apr 30 '23 at 21:47
  • 1
    It's a warning, not an error. Warnings are to discourage error-prone practices that are caused by code that is likely to express something that was not intended. In most cases you'd expect the intention here was to override the virtual method, not to shadow it with an unrelated overload. – Patrick Roberts Apr 30 '23 at 21:48
  • 3
    Try `int main() { A a; Lower l; l.foo(a); }`. – HTNW Apr 30 '23 at 21:48
  • 1
    The problem is that you won't be able to call `some_lower.foo(some_a)`, which most likely is unintended (hence the warning). Name lookup stops at the first scope in which an entity with the name is found. – user17732522 Apr 30 '23 at 21:48
  • 1
    _" if I call `foo(a)` with an instance of class A I would call the virtual method"_ - except that `virtual void foo(A&);` is hidden, so it wouldn't compile as @HTNW implied. – Ted Lyngmo Apr 30 '23 at 21:50
  • Right. So why bother with a warning, if the compiler will generate an error if I do something wrong anyway? And if I _don't_ use it incorrectly, then it works with no ambiguity and does exactly what I expect. So the warning seems like it uselessly prevents me from doing something I wanted to do (our standards require `-Wall -Werror` of course...). – MadScientist Apr 30 '23 at 22:02
  • You are not guaranteed to get an error when the two argument types can be converted in one or both directions. If you have `foo(int)` in one class and `foo(long)` in the other, a call `foo(42)` *will* compile, but may call the wrong function. – j6t Apr 30 '23 at 22:08
  • @ChrisMM That is not the same thing I'm asking. That's asking why the virtual function is not available. I'm asking why the compiler is warning me about unambiguous overloads that work the way I want. – MadScientist Apr 30 '23 at 22:09
  • @j6t yes, I'm aware, that's why I ensured in my question that no such ambiguity could occur by using two different classes as arguments. Or, in my real environment, it's actually the case that the two methods take different numbers of arguments. – MadScientist Apr 30 '23 at 22:10
  • _"our standards require `-Wall -Werror`"_ - but you must allow for exceptions to the rule? You could silence the warning for this particular case. – Ted Lyngmo Apr 30 '23 at 22:15
  • Yes, I can use the trick Vlad provides below and will likely do so. I just don't understand why I should need to. Also it's a bit annoying because it actually changes the potential behavior of my class (methods that didn't used to be available, now become available). Maybe the compiler is not smart enough to examine the arguments to the methods to determine there really is no ambiguity: maybe it just looks for function `foo` as virtual / non-virtual without investigating its full signature. – MadScientist Apr 30 '23 at 22:19
  • I didn't mean that you should change the class, but to silence the warning only. – Ted Lyngmo Apr 30 '23 at 22:57
  • Yeah. That is not so simple because we need to compile with GCC, Clang, and Visual Studio and they have different methods of doing that. But, it can be done of course using _Pragma etc. – MadScientist Apr 30 '23 at 23:04

1 Answers1

2

The problem is that if you will write for example (I suppose that the functions are public)

A a;
Lower().foo( a );

the compiler will issue an error because the function foo

void foo(B& b);

declared in the class Lower hides the virtual function foo declared in the base class Upper.

You could resolve the problem for example with using declaration

class Lower : public Upper
{
public:
    using Upper::foo;
    void foo(B& b);
};

In this case for objects of the class Lower you could call the both functions foo.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • But I don't understand why the compiler warns about this. If I try to call the wrong thing, I get an error. Then I'll go look at it and say "oh oops I messed up". On the other hand, maybe I want _exactly_ what I wrote, and there's no possible way (that I can see) that the compiler will silently compile my code to do something I don't expect, so what's the point of generating a warning? – MadScientist Apr 30 '23 at 21:56
  • 1
    @MadScientist Most often it is a result of a mistake of a programmer. So the compiler issues a warning. – Vlad from Moscow Apr 30 '23 at 21:58
  • Understood but in this case, the mistake of the programmer generates an error anyway. I get it if the compiler wouldn't otherwise give any diagnostics, that a warning is helpful. But that doesn't seem to be the case here. Anyway, I guess I understand I just thought there must be something more subtle at work, to make it worthwhile to generate a warning. – MadScientist Apr 30 '23 at 22:01
  • @MadScientist Projects and classes can be very compound. So such an error requires much time to recompile a project.:) – Vlad from Moscow Apr 30 '23 at 22:03
  • Well... I don't buy that sorry: it takes as much time for me to recompile to fix this warning as to fix the error :) :) :) – MadScientist Apr 30 '23 at 22:04
  • 1
    @MadScientist The problem is you might not get an error but still not using the code in the manner you though you were giving you unexpected results. Sometimes these type of bugs are very subtle and a warning like this is useful even if the code compiles. Ideally you want no warnings even if the code compiles with them. – NathanOliver Apr 30 '23 at 22:07
  • @NathanOliver that's exactly the question I was asking: **how** can the compiler silently do something unexpected in my example above? It seems completely unambiguous to me and I don't see any way that I wouldn't get either (a) exactly what I asked for, or (b) a compiler error. – MadScientist Apr 30 '23 at 22:13
  • @MadScientist It can't. You're not getting a warning about ambiguity. You are getting a warning that you are hiding the virtual function declared in the base class. Normally that is a mistake, and can lead to bugs. So the compiler is giving you a warning to point out that you might have made a goof. – NathanOliver Apr 30 '23 at 22:16
  • As above, that makes no sense to me. It **won't** lead to bugs, because if I indeed did "make a goof" then I'll get a compiler _error_, which is far more effective than a warning. I don't see the point in the compiler warning me about something that I might do in the future but have not done yet, which if I did actually do it then it would be an error anyway. – MadScientist Apr 30 '23 at 22:23
  • @MadScientist I think I see your confusion. The compiler isn't doing a deep analysis. Its sees you have a virtual function in the base and that you have hidden it in the derived class. That's all the warning needs to trigger. In general, doing so is a bug that may or may not generate an error, so the warning just checks the general case, which is simple and easy to do. The compiler doesn't care if your code is not affected. It detects the pattern and then emits the warning. – NathanOliver Apr 30 '23 at 22:27
  • I agree that must be what's happening. But it doesn't seem like deep analysis is needed to compare the argument lists; simply string comparing the mangled names is sufficient to determine if the functions are ambiguous or not. Maybe a few alternatives for `int`/`char` etc. In fact I even get this warning if the parent class's method is private (maybe it's overloading a method in its own superclass) and can't interfere at all. These things seem pretty simple to check, to me, but I agree it seems GCC is not making that effort. – MadScientist Apr 30 '23 at 22:39