6

It seems that a protected member from a template policy class is inaccessible, even with a class hierarchy which seems correct.

For instance, with the following code snippet :

#include <iostream>
using namespace std;

template <class T>
class A {
  protected:
    T value;
    T getValue() { return value; }
  public:
    A(T value) { this->value = value; }
};

template <class T, template <class U> class A>
class B : protected A<T> {
  public:
    B() : A<T>(0) { /* Fake value */ }
    void print(A<T>& input) {
      cout << input.getValue() << endl;
    }
};

int main(int argc, char *argv[]) {
  B<int, A> b;
  A<int> a(42);
  b.print(a);
}

The compiler (clang on OS X, but gcc returns the same type of error) returns the following error :

Untitled.cpp:18:21: error: 'getValue' is a protected member of 'A<int>'
      cout << input.getValue() << endl;
                ^
Untitled.cpp:25:5: note: in instantiation of member function 'B<int, A>::print' requested here
  b.print(a);
    ^
Untitled.cpp:8:7: note: can only access this member on an object of type 'B<int, A>'
    T getValue() { return value; }
      ^
1 error generated.

The strange thing is that the last note from the compiler is totally correct but already applied since the b object is of type 'B<int, A>'. Is that a compiler bug or is there a problem in the code ?

Thanks

benlaug
  • 2,691
  • 2
  • 17
  • 15
  • 2
    See http://stackoverflow.com/questions/11631777/accessing-a-protected-member-of-a-base-class-in-another-subclass for the answer to this question. – Dan Nissenbaum Mar 29 '13 at 12:24

4 Answers4

10

You have misunderstood the meaning of protected access.

Protected members are callable by derived classes. But only on the base object contained inside the class itself.

For example, if i simplify the problem, using :

class A {
protected:
    void getValue(){}
};

class B : protected A
{
public:
    void print(A& input)
    {
        input.getValue(); //Invallid
    }
};

getValue cannot be called on a "A" object other than the "A" object inside the class itself. This for example is valid.

    void print()
    {
        getValue(); //Valid, calling the base class getValue()
    }

As pointed out by Dan Nissenbaum and shakurov. This is however also valid:

void print(B& input)
{
    input.getValue();
}

This is because we explicitly say that input is a object of B. And the compiler know that all that objects of B has protected access to getValue. In the case when we pass a A&, the object might as-well be a type of C, wich could be derrived from A with private access.

David
  • 818
  • 13
  • 30
  • However, by changing the signature to `void print(B& input)` (i.e., pass a `B` object, rather than an `A` object) - it's legal. (Note: assuming `getValue()` returns something valid, not `void`). – Dan Nissenbaum Mar 29 '13 at 11:52
  • Because encapsulation is class-based and not object-based. Otherwise writing assignment operators would've been a nightmare. – shakurov Mar 29 '13 at 12:02
  • I think the answer is given by @shakurov when he writes **A& input may not be a reference to an instance of B. It may be a reference to another subclass (which may well have getValue() inaccessible**. Because of this, only actual explicit `B` class objects allow access to their protected members. – Dan Nissenbaum Mar 29 '13 at 12:03
  • I think this answer would be technically correct if the sentence "But only on the base object contained inside the class itself" were removed. – Dan Nissenbaum Mar 29 '13 at 12:13
6

Let's forget for a minute about the template and look at this:

 class A {
   protected:
     int value;
     int getValue() { return value; }
   public:
     A(int value) { this->value = value; }
 };

 class B : protected A {
   public:
     B() : A(0) { /* Fake value */ }
     void print(A& input) {
       cout << input.getValue() << endl;
     }
 };

The print() method's implementation is wrong because you can't access non-public member of A inside B. And here's why: from within B, you can only access non-public members of B. Those members may be either inherited or not — it doesn't matter.

On the other hand, A& input may not be a reference to an instance of B. It may be a reference to another subclass (which may well have getValue() inaccessible).

shakurov
  • 2,358
  • 24
  • 27
  • "You can't access non-public member of A inside B." Yes you can. As long as B is derived from A you can access protected members of class A, but only within class A. – David Mar 29 '13 at 12:04
  • @DavidJensen I believe what you're saying is what shakurov means by *inside B* (i.e., corresponding to your mentioning "only within class A"). – Dan Nissenbaum Mar 29 '13 at 12:11
  • Yes but still then he is saying that they cant be accessed and i am saying that that can. – David Mar 29 '13 at 12:16
  • @DavidJensen I read that sentence to mean "*you can't access non-public member of an explicit A object inside B*" - is that how you're reading it? – Dan Nissenbaum Mar 29 '13 at 12:18
3

Member functions of a derived class have access to protected base class members within any object of its type that is passed as an argument so long as the explicitly declared class of the object passed as an argument is that of the the derived class (or a further derived class).

Objects explicitly passed as the base class type cannot have their protected members accessed within the derived class's member functions.

In other words, if we have:

class A
{
protected:
    int x;
}

class B : public A
{
    void foo(B b)
    {
        b.x; // allowed because 'b' is explicitly declared as an object of class B
    }
    void goo(A a)
    {
        a.x; // error because 'a' is explicitly declared as having *base* class type
    }
};

...then the line a.x is not allowed because the explicit type of the argument is A, but the rule for protected access only applies to objects explicitly defined as the same class as the class attempting to access the member. (...Or a class derived from it; i.e., if class Cderives from B, then passing an object explicitly declared as an object of class C will also have x accessible within B member functions.)

The reason for this is given by shakurov when he writes (paraphrasing)

A& input might not be a reference to an instance of B. It may be a reference to another subclass (which may well have getValue() inaccessible)

An excellent explication of this answer is also given here: accessing a protected member of a base class in another subclass.

As a matter of interest, I believe that this comes from the C++ standard here:

11.4 Protected member access [class.protected] 1 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.

Community
  • 1
  • 1
Dan Nissenbaum
  • 13,558
  • 21
  • 105
  • 181
2

Don't get distracted by the template. It has nothing to do with the error. The line in main that the compiler is complaining about creates an object of type B<int, a> and tries to access a protected member. That's not legal, regardless of the type. You can only use protected members from inside a member function or friend function. For example:

struct S {
protected:
    void f();
};

int main() {
    S s;
    s.f(); // error: attempts to call a protected member function
}
Pete Becker
  • 74,985
  • 8
  • 76
  • 165