简体   繁体   中英

Calling protected base class method via this pointer casted to base class in derived class (C++)

To begin with, I know about C++ Standard (ISO/IEC 14882:2003): Section 11.5, Paragraph 1 , and this is not that case (but compliler apparently does not think so).

I try to call protected base class method in derived class method through this pointer, static-casted to base class pointer and have in MSVC2008 error C2248: 'A::f' : cannot access protected member declared in class 'A' .

I have to do this in context of 'curiously recurring template pattern', but I can reproduce this error in simplier code, as follows:

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();

It seems that compiler think of casted this pointer as a pointer to other instance, yeah?

The compiler is wrong in this case, what do you think?


Ok, my real code is more like that:

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(){}
};

I cast this to derived class, and I can't use this->f();


By the way, I see that this code is unsafe for usage like class E : public B<D> {...}; : compilable, but static_cast makes wrong cast.

The compiler is correct. To explicitly access the B::f member function, you can write:

this->B::f();

The relevant language is:

11.4 Protected member access [class.protected]

[...] Access to a protected member is granted because the reference occurs in a friend or member of some class C. [...] Access to a protected member [...] involve[s] a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.

Thus protected member access via a cast to the base class B violates this grant, and is disallowed. It is also unnecessary for the reason that you can use this->B::f() as above.


In the case with your actual CRTP motivation, you are correct that you cannot call f() without a static_cast , since D is not a base class of B<D> (the inheritance relationship is in the other direction). Since D is not a base class of B<D> , you cannot call its protected methods from B<D> anyway. One simple workaround is to friend B<D> to D and use the static_cast on the this pointer:

template<typename T>
class B {
public:
    void g() {
        static_cast<T *>(this)->f();
    }
};

class D : public B<D>
{
    friend class B<D>;
    ...

If giving B access to the private parts of D worries you, you can move the private parts to another base class and isolate the CRTP mechanism in D :

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>;
};

Here B<D> is prevented from calling C::h as friendship is neither inherited nor transitive.

I think the compiler is right.

Suppose the following:

void g()
{
    B *b1 = this;
    B *b2 = GetUnrelatedB();
    b1->f(); //Error?
    b2->f(); //Error!
}

The b1 case is equivalent to your static_cast but it would be very strange that b1 will be allowed and b2 will not.

Citing your paragraph 11.5:

[...] the access must be through a pointer to, reference to, or object of the derived class itself.

But static_cast<B*>(this) is of type B* , not D* , no matter that the object itself is the same. Actually, the value of the pointer is irrelevant to this issue, only the type of the expression:

void g()
{
    B *b2 = GetUnrelatedB();
    static_cast<D*>(b2)->f(); //ok!
}

But, how would the compiler know that you are inside a class derived from B once you apply static_cast on this ? In my (humble) opinion, if I create a B object, I expect not to be allowed to call private or protected B methods on the B object, since we don't want to violate encapsulation. It would not matter where the B object is created, as long as it's outside of the B class methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM