Search code examples
c++inheritanceoverloadingclass-template

can't find overloaded method from inherited class template


This is the first time I am using class templates so please don't be to harsh if I made a simply mistake.

I have a class template class A<class T>. It has a method init() that is pure virtual and therefore will be implemented separately in every derived class. What all these possible derived classes will have in common is an init(T* i_x) which basically does some general stuff and then calls the init(). Because this will be the same for every derived class I want to define it in the base class template already. But somehow my compiler doesn't find the right function.

If I try to use the init(T* i_x) on an object of a derived class A_der I get the error:

no matching function for call to 'A_der::init(B_der*)

The classes used for the template parameter T will all be derived from another class B. Therefore the error message involves the class B_der which is derived from class B.

I boiled the problem down to a small example, which should involve everything that is important for the problem. If I try to compile this example in Visual Studio (normally I work in STM32CubeIDE) I get the following error

Severity Code Description Project File Line Suppression State Error C2660 'A_der::init': function does not take 1 arguments template_class-overload_inherited_method [...]\main.cpp 8

So somehow the only function the compiler finds at this point is init() but not the base class template method init(T* ).

Can somebody please tell me why it is like that and what can I do to get the behaviour I want (without implementing a similar init(T* ) in every derived class of A?

Here is my example code:

base class template A - declaration - A.hpp

template<class T>
class A
{
protected:
    T* m_x;

public:
    virtual void connect(T* i_x) final;

    virtual void init() = 0;

    virtual void init(T* i_x) final;
};

base class template A - implementation - A.cpp

#include "A.hpp"

template<class T>
void A<T>::connect(T* i_x)
{
    //some checks
    m_x = i_x; //connects object of B to A
}

template<class T>
void A<T>::init(T* i_x)
{
    connect(i_x);
    init();
}

derived class A_der

#include "A.hpp"
#include "B_der.hpp"

#pragma once
class A_der : public A<B_der>
{
    void init() override;
};

void A_der::init()
{
    //Initialization which needs a B_der connected already
}

main.cpp

#include "B_der.hpp"
#include "A_der.hpp"

int main(void)
{
    B_der testB;
    A_der testA;
    testA.init(&testB);

    return 0;
}

For the sake of completeness:

class B
{

};

class B_der : public B
{

};

EDIT - Solved

Thanks a lot for the fast replies. The combination of the comments from @BoP and @Jarod42 solved the problem. I had to unhide the method with using A<B_der>::init (actually renaming might be the more elegant way) and move the implementation of A into A.hpp.

I will offer the updated example which builds successfully with Visual Studio 2019 for me here:

base class A

template<class T>
class A
{
protected:
    T* m_x;

public:
    virtual void connect(T* i_x) final;

    virtual void init() = 0;

    virtual void init(T* i_x) final;
};


template<class T>
void A<T>::connect(T* i_x)
{
    //some checks
    m_x = i_x; //connects object of B to A
}

template<class T>
void A<T>::init(T* i_x)
{
    connect(i_x);
    init();
}

derivad class A_der

A_der.hpp

#include "A.hpp"
#include "B_der.hpp"

class A_der : public A<B_der>
{
public:
    void init() override;

    using A<B_der>::init;
};

A_der.cpp

#include "A_der.hpp"

void A_der::init()
{
    //Initialization which needs a B_der connected already
}

main.cpp

#include "B_der.hpp"
#include "A_der.hpp"

int main(void)
{
    B_der testB;
    A_der testA;
    testA.init(&testB);

    return 0;
}

for completeness

B.hpp

class B
{
};

B_der.hpp

#include "B.hpp"

class B_der : public B
{
};

I also forgot to make the methods of A_der public in the earlier example, this is corrected here. And I removed the #pragma onces in this example.


Solution

  • class A_der : public A<B_der>
    {
        void init() override;
    };
    

    When you declare a function init in the derived class, it hides all things named init from the base class. This is just like when declaring something in an inner scope - it hides things with the same name from outer scopes.

    There are ways to import the hidden names, but an easy solution would be to just chose a different name, like init_base. Or, probably better, pass a parameter to the class constructor.