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?

9
  • 3
    You can only access protected members of this' base (same instance). this and b are not the same instance.
    – jrok
    Commented Sep 4, 2012 at 20:35
  • 2
    @MikeSeymour nope. (but I admit I thought the exact same thing until recently) Commented Sep 4, 2012 at 20:37
  • I'm sure this has been asked many times before, but it's hard to locate the exact duplicate. Commented Sep 4, 2012 at 20:38
  • 1
    By the way, the assignment is not the culprit, but one of them.
    – eq-
    Commented Sep 4, 2012 at 20:38
  • 1
    "Cannot access protected member of base class in derived class" maybe?
    – jrok
    Commented Sep 4, 2012 at 20:45

4 Answers 4

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.

8
  • 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
    Commented Sep 4, 2012 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) Commented Sep 4, 2012 at 20:56
  • Note you can also trick the compiler using a reinterpret_cast. Ugly, and may be dangerous, but...
    – Macmade
    Commented Sep 4, 2012 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. Commented Sep 4, 2012 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
    Commented Dec 28, 2015 at 11:31
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.

2
  • "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
    Commented Dec 28, 2015 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. stackoverflow.com/questions/34588930/…
    – h9uest
    Commented Jan 5, 2016 at 1: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
  // ...
}
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.

1
  • 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 Commented Feb 9, 2015 at 21:19