0

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.

Tobxon
  • 35
  • 4
  • Unrelated: Why do you have a return statement `return true;` inside the function `void A::connect`. This function's return type is `void` so you should not have `return true;` inside it. – Jason Jan 27 '22 at 09:42
  • 1
    `using A::init` should unhide the method. – Jarod42 Jan 27 '22 at 09:47
  • 1
    *"base class template A - implementation - A.cpp"*. See [why-can-templates-only-be-implemented-in-the-header-file](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file). – Jarod42 Jan 27 '22 at 09:48
  • `virtual void connect(T* i_x) final;`. What is the purpose of declaring the base method both `virtual` and `final`? – Jarod42 Jan 27 '22 at 09:51

1 Answers1

0
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.

BoP
  • 2,310
  • 1
  • 15
  • 24