6

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.

1

3 Answers 3

13

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you, but in template version it won't work. this->T::f() in B::g() causes error C2039: 'D' : is not a member of 'B<T>' and error C2662: 'D::f' : cannot convert 'this' pointer from 'B<T>' to 'D &'.
@Yurash see the extended answer.
OK, I see, f() should be public.
It doesn't have to be! You can get CRTP and access control; see the last part of my answer.
@ecatmur: Thank you. I'm reluctant to use friend, but in this case it really could be a right solution. Base class is somewhat stable, and I'm not so afraid that it would do something bad to the derived one.
1

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

3 Comments

In template version (see above) I cast to derived class.
@Yurash: But that makes no sense! You are trying to access to the derived member from the base class. protected is supposed to allow the other way around.
OK, you are right. My first example was not appropriate for the real problem.
0

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.

1 Comment

static_cast should somehow know to what it casts. It won't cast to unrelated type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.