通过此指针调用受保护的基类方法,该指针在派生类(C ++)中转换为基类

时间:2012-08-03 12:01:32

标签: c++ inheritance compiler-errors protected

首先,我了解 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会导致错误的转换。

3 个答案:

答案 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类方法之外。