При получении класса из аргумента шаблона производный класс не имеет защищенного доступа к члену, даже если он возвращает true из std::is_base_of
например:
class A
{
protected:
virtual void Do()
{
std::cout << "A::Do()";
}
};
class B : public A
{
protected:
virtual void Do()
{
std::cout << "B::Do() - "; A::Do();
}
};
и шаблон класса
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;
};
если мы затем создадим экземпляр и вызовем
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()
}
приведение указателя BaseClass к указателю типа C позволяет нам правильно вызывать функцию, так как мы можем вызывать функцию защищенного члена, и корректно вызывается переопределение.
такого рода вещи не случаются с классом B, который может вызывать A :: Do () напрямую, как это происходит от A.
C является производным от BaseClass, но не имеет доступа к защищенным функциям-членам.
указатель на BaseClass действует как внешний указатель, а не вызывается классом шаблона.
Таким образом, общий вопрос:
- это законный / безопасный / хороший код?
- есть причина почему?
- Есть ли лучший способ сделать это?
изменить для более подробной информации и рассуждений:
в этом случае C
- это класс-оболочка, который позволяет мне перенаправлять вызовы функций экземпляра B
, добавляя при этом функциональность. отсюда необходимость хранить экземпляр B
, так как он содержит переменные, которые будут обновлены, а B
- это конкретный экземпляр, который я храню или извлекаю из него и специализируюсь на нем.
если бы у нас было
class D : public B
{
protected:
virtual void Do()
{
std::cout << "D::Do()";
}
};
и мы создали так
D d;
C<B> c(&d)
Я ожидал бы, когда позвоню c.Do()
, что он вызовет специализацию D
, и в этом конкретном случае.
как бы то ни было, похоже, мне нужно сделать C другом BaseClass следующим образом:
// forward declared as they live separately
template <class BaseType> class C;
class B
{
...
friend class C<B>;
};