从模板参数派生类时,即使派生类从std::is_base_of
返回true,派生类也没有受保护的成员访问权限。
例如:
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不会发生这种情况,类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>;
};
答案 0 :(得分:3)
对于m_Base
类型的A*
,这是非法的。
m_Base->Do();
除非该对象是*this
,否则您无法访问该对象的受保护成员。这是一个巨大的简化,但它有99%的时间有效。
作为解决方案,您可以将template<class T> class C
中的friend
设为A
:
//define C beforehand
class A
{
friend template<class T> class C;
protected:
virtual Do()
{
std::cout << "A::Do()";
}
};
最后,请注意
m_Self(static_cast<C<BaseClass>*>(c))
是非法的:c
是BaseClass
(在您的使用中,从&b
实例化),您无法将其强制转换为C<BaseClass>*
。
答案 1 :(得分:3)
派生与组成之间存在混淆:派生类是基类,组成类具有作为成员。在这里,您的类既是派生的又是组成的,类c
的{{1}}是C<A>
,但也有一个指向{{1 }}类A
。
调用b
时,将调用B
的方法c.Do1
,在您的示例中为Do
(由于BaseClass
),并且您致电A
。
调用C<A>
时,将调用A::Do
的方法c.Do2
,该方法受保护!
调用Do
时,将调用b
的方法c.Do3
,该方法是指向Do
的指针,因此调用m_self
。
我认为您应该只删除两个成员b
和B::Do
,您的构造函数将为:
m_Base
那么您将拥有:
m_Self
您还可以在C: BaseClass() {}
中覆盖C<A> c;
c.Do(); // calls A::Do
C<B> d;
d.Do(); // calls B::Do
。
答案 2 :(得分:-1)
您不需要存储BaseClass的指针。 C
无法访问m_base,因为Do()
受保护,这意味着外部是私有的。
您可以致电
BaseClass::Do()
因为在这种情况下是公共的(您正在调用父类的方法)。
除了存储指针外,您的方法还用于静态多态和基于策略的设计。
您可以在这篇文章中找到更多信息: C++: Inherit class from template parameter