Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Policy inheritance and inaccessible protected members

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

like image 338
benlaug Avatar asked Mar 29 '13 11:03

benlaug


2 Answers

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.

like image 111
David Avatar answered Sep 27 '22 18:09

David


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

like image 44
shakurov Avatar answered Sep 27 '22 17:09

shakurov