multiple inheritance with shared attribute names

I have come here many times to research issues that others have also had, but have not found a topic directly related to this so figured I'd start my own.

The question I'm having is why are protected attributes in a base class not visible if accessed by dynamic_cast? This seems to be the best way to access attributes if names are shared between multiple base classes (since direct access using this-> would not know which instance is being referred to), however it does not seem to be allowed.

Here is a basic example of what I am referring to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Base1
{
public:
    Base1(void) : prot(1) {}
protected:
    int prot;
};

class Base2
{
public:
    Base2(void) : prot(2) {}
protected:
    int prot;
};

class Derived : public Base1, public Base2
{
public:
    int fprot1(void) {return dynamic_cast<Base1*>(this)->prot;}
    int fprot2(void) {return dynamic_cast<Base2*>(this)->prot;}
};

Derived d;
cout << "Base1 prot = " << d.fprot1() << endl;
cout << "Base2 prot = " << d.fprot2() << endl;


Attempting to compile gives the following errors:
error C2248: 'main::Base1::prot' : cannot access protected member declared in class 'main::Base1'
error C2248: 'main::Base2::prot' : cannot access protected member declared in class 'main::Base2'

Even though dynamic_cast seems to lose protected access, it is still available as expected if I change class Derived to the following:

1
2
3
4
5
6
class Derived : public Base1, public Base2
{
public:
    int fprot1(void) {return Base1::prot;}
    int fprot2(void) {return Base2::prot;}
};


This to me doesn't seem as good though, as it can very quickly get confusing accessing static members or members that may not yet be instantiated (in the case of using the same context to access attributes or methods outside the class scope).

Is there a reason dynamic_cast cuts off protected access? Is this supposed to be the case? If so, is the only workaround what I have posted or are there better solutions?

Thanks in advance for the help!
Last edited on
Is there a reason dynamic_cast cuts off protected access? Is this supposed to be the case?


Yes. What's happening is you're no longer accessing 'this' directly. You're accessing it through another pointer.

Think of it this way:

1
2
Base1* ptr = this;  // also note, no need for dynamic cast for upcasting
return ptr->prot;    //  dynamic cast is wasteful for that - it has more overhead 


On line 2 here, there's no guarantee that 'ptr' points to this. Obviously we know it does, but it could in theory point to another object.

The zinger is that protected grants access to this object only. You can't access's another object's protected members. And since 'ptr' is potentially accessing another object, it's disallowed which is why you get the error.

If so, is the only workaround what I have posted or are there better solutions?


I'm still not entirely sure why you don't like the Base1::prot option. That's exactly what the scope operator is for: specifying scope. Here you want prot in the Base1 scope, so logically you'd want Base1::prot.

Doesn't get much more straighforward or simpler than that. These weird casting things and pointer tricks you're trying to do are kind of hackish.
Thanks for the explanation Disch! Isn't casting up (forcefully) the entire point of dynamic_cast? The scope operator is very straighforward and I use it often, I just thought I would use dynamic_cast here to avoid confusion with using scope operators to access static attributes/methods which are not bound to an instance of the class. Then I ran into this issue and was wondering why. With a little better understanding now I will just stick to the scope operators I originally had.

Thanks again!
Last edited on
I just found the relevant paragrah in the standard - but Dish has already posted the answer - but I'll post the quote from the standard anyway:
11.5 Protected member access
When a friend or a member function of a derived class references a protected nonstatic member function or
protected nonstatic data member of a base class, an access check applies in addition to those described earlier
in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a
pointer to, reference to, or object of the derived class itself (or any class derived from that class)
Great, thanks for the reference!
Isn't casting up (forcefully) the entire point of dynamic_cast?


Nope. A Derived will always have a Parent, therefore casting up from Derived to Parent is always safe and dynamic cast isn't necessary. (All Poodles are Dogs)

dynamic_cast is for when you need to cast down from Parent to Derived.

Since Parent doesn't necessarily have a Derived, dynamic_cast needs to check to make sure it does. This adds some overhead to do the runtime check. (not all Dogs are Poodles)

Consider the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Parent
{
// assume polymorphic
};

class A : public Parent { };
class B : public Parent { };

void func(Parent* p)
{
  // do we have an 'A'?
  A* a = dynamic_cast<A*>(p);
  if(a)
  {
    a->DoSomethingThatOnlyACanDo();
  }
  else
    cout << "error message:  passed pointer is not an A";
}

int main()
{
  A a;
  B b;

  func(&a);  // OK, calls our special function
  func(&b);  // gives us the error message
}



EDIT:

Also note that downcasting is very rarely needed and if you find yourself doing it, you're probably doing something wrong.
Last edited on
Ah hah! That makes much more sense, thanks!!
Topic archived. No new replies allowed.