When deriving a class from a template argument, the derived class doesn't have protected member access, even though it returns true from std::is_base_of
eg:
class A
{
protected:
virtual void Do()
{
std::cout << "A::Do()";
}
};
class B : public A
{
protected:
virtual void Do()
{
std::cout << "B::Do() - "; A::Do();
}
};
and the template class
template <class BaseClass>
class C : public BaseClass
{
public:
C(BaseClass* c) :
m_Base(c),
m_Self(static_cast<C<BaseClass>*>(c))
{}
// this will never call the passed in class version
void Do1()
{
BaseClass::Do();
}
// this has the error 'virtual int A::Do()' is protected within this context
void Do2()
{
m_Base->Do();
}
void Do3()
{
m_Self->Do();
}
BaseClass* m_Base;
C<BaseClass>* m_Self;
};
if we then instantiate and call
int main()
{
A a;
B b;
C<A> c(&b);
std::is_base_of<A, C<A> >::value; // this is true
c.Do1(); // calls A::Do()
c.Do2(); // throws a protected access error
c.Do3(); // calls B::Do()
}
casting the BaseClass pointer to a pointer of type C allows us to call the function correctly, both in that we can call the protected member function, and the override is correctly called.
this kind of thing doesn't happen with class B, which can call A::Do() directly, as it is derived from A.
C is derived from BaseClass, yet does not have access to the protected member functions. the pointer to BaseClass acts like it is an external pointer, not being called by the template class.
the overall question is therefore:
- is this legal/safe/good code?
- is there a reason why?
- is there a better method to doing this?
edit for some more details and reasoning:
in this case, C
is a wrapper class that allows me to forward on the function calls of an instance of B
, whilst adding functionality to it. hence the need to store the instance of B
, as its contains variables that will be updated, and B
is the concrete instance that i store - or derive from and further specialise.
if we had
class D : public B
{
protected:
virtual void Do()
{
std::cout << "D::Do()";
}
};
and we created as thus
D d;
C<B> c(&d)
i would expect when i call c.Do()
that it calls the D
specialisation, and on that particular instance.
however this goes, it does seem that i need to make C a friend of BaseClass as follows:
// forward declared as they live separately
template <class BaseType> class C;
class B
{
...
friend class C<B>;
};