0

I'm trying to call overloaded virtual function which fails with "no matching function" error.

Here's mwe.

#include <iostream>
#include <string>
#include <sstream>

class A {
    public:
        virtual int foo(int a, int b) {
            // interface
            return 1;
        }

    protected:
        virtual int foo(std::string a, std::string b, std::string c) {
            // actual work
            std::cout << "A::foo(str...) " << a << " " << b << " " << c << std::endl; 
            return 0;
        }
};

class B : public A {
    public:
        virtual int foo(int a, int b) override {
            // some work. e.g. parse arguments
            foo("a", "b", "c"); // no matching function call
            return 0;
        }
};

int main(void) {
    B b;
    A* bb = static_cast<A*>(&b);
    bb->foo(1, 2);
}

I expect interface foo(int, int) to be called as normal virtual function. If object is of derived class, it should call foo(int, int) override which, in it's turn calls foo (string, string, string) as normal virtual function. Since it's not overriden in B, A::foo(string, string, string) should be called.

I'm getting "no matching function call" instead.

On the other hand, if I override foo(string, string, string) in B, virtual functions & inheritance work as I would expect. Override works.

mwe:

#include <iostream>
#include <string>
#include <sstream>

class A {
    public:
        virtual int foo(int a, int b) {
            // interface
            return 1;
        }

    protected:
        virtual int foo(std::string a, std::string b, std::string c) {
            // actual work
            std::cout << "A::foo(str...) " << a << " " << b << " " << c << std::endl; 
            return 0;
        }
};

class B : public A {
    public:
        virtual int foo(int a, int b) override {
            // some work. e.g. parse arguments
            foo("a", "b", "c");
            return 0;
        }
    protected:
        virtual int foo(std::string a, std::string b, std::string c) {
            // actual work
            std::cout << "B::foo(str...) " << a << " " << b << " " << c << std::endl; 
            return 0;
        }
};

int main(void) {
    B b;
    A* bb = static_cast<A*>(&b);
    bb->foo(1, 2);
}

so in first example call to foo(string, string, string) fails to find A::foo(string, string, string), but in second example override does it with no issues. So the function is in virtual function table.

So what happens in first case?

Roman
  • 1,396
  • 4
  • 15
  • 39
  • Design-wise, you have it flipped. Your interface should be non-virtual, and the actual work can be handled polymorphically. The pattern is called NVI. – sweenish Jul 21 '22 at 20:40
  • Well, I'm restricted by legacy. But anyway, now I'm interested in either me not knowing how basic c++ staff works, or it not working as it should. – Roman Jul 21 '22 at 20:42
  • 1
    Well, I wouldn't say this is basic C++. It's just one of many arcane rules of overload resolution. – Sam Varshavchik Jul 21 '22 at 20:46
  • Ok, I had a look at name lookup thread. So, as far as I understand, we look for name foo (just the name, not the signature), and then overloading works amongst all foo's found in b. The name lookup stops as soon as there's all foo's found in B. On the other hand, if I override function, override is found inside B. I'll add one more link https://stackoverflow.com/questions/55140861/why-does-the-member-function-name-lookup-stay-in-the-parent-class – Roman Jul 21 '22 at 20:52
  • 1
    This is because overload resolution conceptually happens in one scope at a time. Add `using A::foo` into `B`. More details here: https://isocpp.org/wiki/faq/strange-inheritance#overload-derived – fukanchik Jul 21 '22 at 20:53
  • Personally, I think this 'name hiding' business has always been a mistake. I know there are arguments for and against, but, to me, it violates the 'principle of least surprise '. – Paul Sanders Jul 21 '22 at 21:37

0 Answers0