3

Possible Duplicate:
Accessing protected members in a derived class

If I have an abstract base class and a concrete templated class that derives from it, which has a method that uses a pointer to the base class - it seems that the derived class stops seeing itself as derived from it:

class AbstractBase
{
protected:
    virtual void test() = 0;
};

template < class T >
class Derived : public AbstractBase
{
public:
    virtual void call( AbstractBase* d ) { d->test(); }  //  Error!
protected:
    virtual void test() {}
};

int main()
{
    Derived< int > a;
    Derived< int > b;

    b.call( &a );

    return EXIT_SUCCESS;
}

This errors with:

'virtual void AbstractBase::test()' is protected

The compiler's not wrong, it's definitely protected - but if Derived< T > inherits from AbstractBase, why is it complaining?

Community
  • 1
  • 1
cmannett85
  • 21,725
  • 8
  • 76
  • 119
  • 3
    This isn't related to templates. Make Derived a non-template class and you'll get the same error. – Mat Aug 04 '12 at 14:45
  • +1 You're right, but in my ignorance it's what I searched for when I came across the problem so I'll leave the question as is for others who do the same. – cmannett85 Aug 04 '12 at 15:24

2 Answers2

3

The reason it isn't allowed is because AbstractBase as a type declares test to be protected. This makes it private to all unless the current class is a direct descendant of AbstractBase. Even so, that class can only access the member though an object of the same class, not a different descendant, and not directly from AbstractBase itself.

template < class T >
class Derived : public AbstractBase
{
public:
    virtual void call( Derived * d ) {
        d->test(); // ok, d has same type as this
        AbstractBase *b = this;
        b->test(); // not ok
    }
protected:
    virtual void test() {}
};

You can, as demonstrated above, just allow it for pointers of the same type. Alternatively, you can create a proxy base class for Derived to implement your virtual method to call test. This will allow access from different Derived types.

class DerivedBase : public virtual AbstractBase
{
public:
    virtual void call( DerivedBase * d ) { d->test(); }
};

template < class T >
class Derived : public DerivedBase
{
protected:
    virtual void test() {}
};

And can be accessed this way:

   Derived< int > a;
   Derived< int > b;
   Derived< float > c;

   b.call( &a );
   c.call( &a );
jxh
  • 69,070
  • 8
  • 110
  • 193
  • I can't perform the first option on my 'real' code for structural reasons, but it's duly noted. As for the second, can you explain why that works? To my eyes it's just another layer of abstraction. – cmannett85 Aug 04 '12 at 15:12
  • Forget the template. It won't work. You're work around works, I'm changing to that. – jxh Aug 04 '12 at 15:18
  • @cbamber85: The reason the abstraction layer works is that it becomes a common base class that provides a public interface for `call` that calls the protected `test` method. All `Derived` then inherits from `DerivedBase` instead. – jxh Aug 04 '12 at 15:23
1

This is not related to templates, but to protected member access in general. See section 11.4 Protected member access [class.protected] of the latest publicly available draft C++ Standard

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2) 115 As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve 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.

[Example:

class B {
protected:
    int i;
    static int j;
};

class D1 : public B {
};

class D2 : public B {
    friend void fr(B*,D1*,D2*);
    void mem(B*,D1*);
};

void fr(B* pb, D1* p1, D2* p2) {
   pb->i = 1; // ill-formed
   p1->i = 2; // ill-formed
   p2->i = 3; // OK (access through a D2)
   p2->B::i = 4; // OK (access through a D2, even though
                 // naming class is B)
   int B::* pmi_B = &B::i; // ill-formed
   int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*)
   B::j = 5; // OK (because refers to static member)
   D2::j = 6; // OK (because refers to static member)
}
TemplateRex
  • 69,038
  • 19
  • 164
  • 304