21

I have the following code:

struct A {
protected:
    A() {}

    A* a;
};

struct B : A {
protected:
    B() { b.a = &b; }

    A b;
};

It strangely doesn't compile. The culprit is the b.a = &b; assignment: both GCC and clang complain that A() is protected, which shouldn't be a problem because B inherits A. Which dark corner of the standard have I come into?

  • 3
    You can only access `protected` members of `this`' base (same instance). `this` and `b` are not the same instance. – jrok Sep 04 '12 at 20:35
  • 2
    @MikeSeymour nope. (but I admit I thought the exact same thing until recently) – Luchian Grigore Sep 04 '12 at 20:37
  • I'm sure this has been asked many times before, but it's hard to locate the exact duplicate. – Sergey Kalinichenko Sep 04 '12 at 20:38
  • 1
    By the way, the assignment is not *the* culprit, but one of them. – eq- Sep 04 '12 at 20:38
  • @jrok: It looks like you're right. It seems I don't use inheritance enough to have ever encountered this dark corner. – Mike Seymour Sep 04 '12 at 20:43
  • @dasblinkenlight: Especially as the titles of such questions are likely to be non-descriptive (as is here too). Does anyone have a suggestion for a better title? –  Sep 04 '12 at 20:44
  • 1
    "Cannot access protected member of base class in derived class" maybe? – jrok Sep 04 '12 at 20:45
  • @jons34yp Unfortunately, this is one of these questions where coming up with a descriptive title is nearly equivalent to finding the right answer :) – Sergey Kalinichenko Sep 04 '12 at 20:47
  • possible duplicate of [Protected data in parent class not available in child class?](http://stackoverflow.com/questions/1414506/protected-data-in-parent-class-not-available-in-child-class) – Bo Persson Sep 04 '12 at 21:03

4 Answers4

21

The meaning of protected is that the derived type will have access to that member of its own base and not of any random object*. In your case, you care trying to modify b's member which is outside of your control (i.e. you can set this->a, but not b.a)

There is a hack to get this to work if you are interested, but a better solution would be to refactor the code and not depend on hacks. You could, for example, provide a constructor in A that takes an A* as argument (this constructor should be public) and then initialize it in the initializer list of B:

A::A( A* p ) : a(p) {}
B::B() : b(&b) {}

* protected grants you access to the base member in any instance of your own type or derived from your own type.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    +1: I always hear the saying "access-control works on a per-class basis, not a per-object basis", but it seems `protected` is the one exception to this rule? – Jesse Good Sep 04 '12 at 20:53
  • 1
    @JesseGood: Not really, it is still per-class. I was maybe too vague in the answer. You have access to the member in any object of type `B` or derived from `B`, just not in objects of type `A` or derived from `A` (that are not `B`) – David Rodríguez - dribeas Sep 04 '12 at 20:56
  • Note you can also trick the compiler using a reinterpret_cast. Ugly, and may be dangerous, but... – Macmade Sep 04 '12 at 21:04
  • @Macmade: `reinterpret_cast` as a *general recommendation* is dangerous, as it depends on the types having standard layout and the objects being aligned (i.e. will only work with the first base, and not always). A `static_cast` might actually be a better option, although it is still dangerous (virtual bases), it will at least adapt the offset of the base subobject (if it can) or... well, it is all undefined behavior anyway. At any rate, `reinterpret_cast` is a really bad recommendation for almost anything. – David Rodríguez - dribeas Sep 04 '12 at 21:15
  • "You have access to the member in any object of type B or derived from B, just not in objects of type A or derived from A (that are not B) " --- Hi David. I'm having a design in which the derived class(derived from base, of course) has multiple instances of base class(composition). In a derived class function, I tried to access the protected members of those base instances, but got compilation errors. Doesn't it conflict with the very meaning of protected?(access control works on per class basis) – h9uest Dec 28 '15 at 11:31
  • @h9uest: You should probably write up a real question with the exact layout. It is unclear what you mean with *multiple instances of base class (composition)*. Either you inherit from it and it is a base, or you use composition and is a member, even if you also inherit from the same base. – David Rodríguez - dribeas Dec 28 '15 at 15:03
  • @DavidRodríguez-dribeas Hi David. Happy new year. I formulated my question here: http://stackoverflow.com/questions/34588930/c-protected-fail-to-access-bases-protected-member-from-within-derived-class – h9uest Jan 04 '16 at 10:25
  • @DavidRodríguez-dribeas I don't understand your example. A::A is not used in the implementation of B::B, and B could not use a member defined as 'A* a;' to access protected members of 'a' anyway. What is 'b(&b)', member 'b' appears to be being initialised with its own address? – Neutrino Apr 15 '16 at 13:20
2

There are actually two separate problems here.

The first is that the line doesn't just do an assignment, but tries to initialize the base class (which works fine) and the member b. To create the b member it needs to construct it, and as a member it needs public access to a constructor, which it doesn't have.

Then the assignment also is unable to access non-public member of b because again, it's not of type B but type A instead.

Remember that protected means you can access parts of A through a B object (or child) only.

In this case tell us your real problem and we can try to help solve it. Inheriting and composing from the same type is a design smell.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • "Inheriting and composing from the same type is a design smell." why is it? Can you elaborate? I'm recently having a design and in the derived class, there are several instances of base(composition). In such a scenario, I need to access protected members of those base instances from within my derived class functions - compilation errors there. If the protected access specifier are per class(rather than per instance), this should be allowed - why not? – h9uest Dec 28 '15 at 11:20
  • Hi Mark. I prepared a specific question below, would you please take a moment to enlighten me?(The standard did not say anything similar to "accessing base THROUGH a derived object"; clause 11 on page 237 clearly states a protected name can be used by classes derived from that class. In other words, it works on a class basis and instance does not come into the picture.) Cheers. http://stackoverflow.com/questions/34588930/c-protected-fail-to-access-bases-protected-member-from-within-derived-class/34589641?noredirect=1#comment56950922_34589641 – h9uest Jan 05 '16 at 01:39
0

All the compilers that I tested complained about several things, and specifically the protected constructor would be a problem even if the assignment statement were removed.

You don't get to access the protected members of any instance of a type you derive from. This issue is clarified in the examples of 11.4p1.

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

class D1 : public B {
};

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

void D2::mem(B* pb, D1* p1) {
  pb->i = 1; // ill-formed
  p1->i = 2; // ill-formed
  // ...
}
eq-
  • 9,986
  • 36
  • 38
0

It seems like a big limitation of the C++ language. How would you solve problem like this:

class Node
{
public:
 void Save();
protected:
 virtual void SaveState(int type) = 0;
};

class BinaryNode : public Node
{
protected:
 Node *left;
 Node *right;

 virtual void SaveState(int type) override
 {
    left->SaveState(type);
    right->SaveState(type);
 }
};

In this example I do not want to make method SaveState visible outside Node hierarchy. Only method Save should be public.

Peter
  • 346
  • 5
  • 7
  • If a "Node" hides details of saving process from the user then you shouldn't influence on it in "BinaryNode", because you don't know exact type of "Node *" and you cannot guess about its internal implementation. In other case you need to add some arguments for "Save" in order to influence on it from the outside or make your example more clear – bartolo-otrit Feb 09 '15 at 21:19