Home > front end >  C inherited method not seen from within derived class
C inherited method not seen from within derived class

Time:11-06

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:

  1. Why did Base::f(EmptyA) become inaccessible from Derived::handle?
  2. How can I fix this code so that every call to Derived::handle is forwarded to the most specific version of method f?

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 type EmptyA only and there is no version of handle() inside the Derived class that takes parameter of type DerDerEmptyA . So both of your calls to handle() will call the same Base::f that takes EmptyA 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;

    //...
};
  • Related