从模板参数派生类时,派生类没有 protected 成员访问权限,即使它从 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 类型的指针允许我们正确调用该函数,因为我们可以调用 protected 成员函数,并且可以正确调用覆盖。
这种事情不会发生在类 B 上,它可以直接调用 A::Do(),因为它是从 A 派生的。
C 派生自 BaseClass,但无权访问 protected 成员函数。 指向 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>;
};
最佳答案
这对于 m_Base
是非法的类型 A*
.
m_Base->Do();
除非所述对象为 *this
,否则您无法访问对象的 protected 成员.这是一个巨大的简化,但它在 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>*
.
关于c++ - 从模板参数派生的类没有 protected 成员访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56079961/