Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 546
Yurash Avatar asked Aug 03 '12 12:08

Yurash


People also ask

Can a base class pointer call methods in the derived class?

When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.

How do you cast base class pointers to derived class pointers explain?

Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.

Can a derived class pointer point to a base class object?

Derived class pointer cannot point to base class.

Can a derived class directly access a protected member of its base class?

Protected members in a class are similar to private members as they cannot be accessed from outside the class. But they can be accessed by derived classes or child classes while private members cannot.


1 Answers

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

this->B::f();

The relevant language is:

c++11

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.

like image 179
ecatmur Avatar answered Sep 21 '22 18:09

ecatmur