Search code examples
c++inheritancecompiler-errorsprotected

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.


Solution

  • 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.