首先,我了解 C ++标准(ISO / IEC 14882:2003):第11.5节,第1段,这不是那种情况(但是compliler显然不这么认为)。
我尝试通过 this 指针在派生类方法中调用受保护的基类方法,静态转换为基类指针,并在MSVC2008 错误C2248:'A :: f'中:无法访问在类'A'中声明的受保护成员。
我已经在“奇怪的重复模板模式”的上下文中执行此操作,但我可以在更简单的代码中重现此错误,如下所示:
class B
{
protected:
void f(){}
};
class D : public B
{
public:
void g()
{
f(); // ok
this->f(); // ok
static_cast<B*>(this)->f(); // C2248 in MSVC2008
dynamic_cast<B*>(this)->f(); // C2248
((B*)this)->f(); // C2248
}
};
D d; d.g();
似乎编译器认为将这个指针作为指向其他实例的指针,是吗?
在这种情况下编译器是错误的,你怎么看?
好的,我的真实代码更像是:
template<class T>
class B
{
public:
void g()
{
f(); // error C3861: 'f': identifier not found
this->f(); // error C3861: 'f': identifier not found
// static_cast to derived class
static_cast<T*>(this)->f(); // C2248 in MSVC2008
}
};
class D : public B<D>
{
protected:
void f(){}
};
我将此投射到派生的类,我无法使用 this-&gt; f();
顺便说一下,我发现这段代码对于像class E : public B<D> {...};
这样的用法是不安全的:可编译,但是static_cast会导致错误的转换。
答案 0 :(得分:13)
编译器是正确的。要显式访问B::f
成员函数,您可以编写:
this->B::f();
相关语言是:
11.4受保护的成员访问[class.protected]
[...]授予对受保护成员的访问权限,因为该引用发生在朋友或某些成员中 C类[...]对受保护成员的访问[...]涉及[可能是隐含的]对象表达式(5.2.5)。在这种情况下, 对象表达式的类应为C或从C派生的类。
因此,通过强制转换为基类B
的受保护成员访问会违反此授权,并且不允许。您也可以不必使用上述this->B::f()
。
如果您的实际CRTP动机是正确的,那么您不能在没有f()
的情况下致电static_cast
,因为D
不是B<D>
的基类(继承关系是另一个方向)。由于D
不是B<D>
的基类,因此无论如何都无法从protected
调用其B<D>
方法。一个简单的解决方法是friend
B<D>
到D
并在static_cast
指针上使用this
:
template<typename T>
class B {
public:
void g() {
static_cast<T *>(this)->f();
}
};
class D : public B<D>
{
friend class B<D>;
...
如果B
private
D
部分对private
部分感到担忧,您可以将D
部分移到另一个基类,并在{{{{{}}中隔离CRTP机制1}}:
template<class T> class B {
public:
void g() {
static_cast<T*>(this)->f();
}
};
class C {
private:
void h();
protected:
void f(){ std::cout << "D::f\n"; }
};
class D: protected C, public B<D>
{
friend class B<D>;
};
由于友谊既不是继承也不是传递,因此B<D>
无法调用C::h
。
答案 1 :(得分:1)
我认为编译器是对的。
假设以下内容:
void g()
{
B *b1 = this;
B *b2 = GetUnrelatedB();
b1->f(); //Error?
b2->f(); //Error!
}
b1
案例与您的static_cast
相同,但允许b1
并且b2
不会这样做会非常奇怪。
引用你的第11.5段:
[...]访问必须通过指向,引用或派生类本身的对象。
但static_cast<B*>(this)
的类型为B*
,而不是D*
,无论对象本身是否相同。实际上,指针的值与此问题无关,只与表达式的类型相关:
void g()
{
B *b2 = GetUnrelatedB();
static_cast<D*>(b2)->f(); //ok!
}
答案 2 :(得分:0)
但是,一旦你在static_cast
上应用this
,编译器如何知道你在一个派生自B的类中?在我(谦虚)的意见中,如果我创建一个B
对象,我希望不允许在B
对象上调用私有或受保护的B
方法,因为我们不想要违反封装。 B
对象的创建位置无关紧要,只要它在B
类方法之外。