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 once
s in this example.
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.