Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic downcast on private inheritance within private scope

A tweak on this question that I've run into. Consider:

class A {};

class B : private A {
   static void foo();
};

void B::foo(){
   B* bPtr1 = new B;
   A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
   B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}

Since aPtr1 is, in fact, of type B*, and since we've got full access to B and its inheritance from A, I'd expected both casts to work. But they don't; why? Is there another way to achieve this cast?

Note that:

  • If foo() were not a member of B, both casts would fail.
  • If B inherits from A publicly, both casts would work.
like image 316
Ziv Avatar asked Aug 03 '11 14:08

Ziv


People also ask

When inheritance is private the private members are?

Private Inheritance is one of the ways of implementing the has-a relationship. With private inheritance, public and protected member of the base class become private members of the derived class. That means the methods of the base class do not become the public interface of the derived object.

When inheritance is protected private members of base class are in the derived class?

public, protected and private inheritance in C++ protected inheritance makes the public and protected members of the base class protected in the derived class. private inheritance makes the public and protected members of the base class private in the derived class.

What type of relationship does private inheritance model?

Private inheritance creates a “is implemented in terms of” relationship between classes, also known as “has-a” relationship. By using composition, you can create the same kind of relationship but with a much looser coupling between the classes.

What is the use of private inheritance in C++?

Instead, private inheritance refers to the idea of being "implemented in terms of a". The key difference is that whereas public inheritance provides a common interface between two classes, private inheritance does not--rather, it makes all of the public functions of the parent class private in the child class.


1 Answers

5.2.7 (ISO/IEC 14882, 12/29/2003) is pretty explicit on this point:

[about the expression dynamic_cast<T>(v)]

If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a pointer to the unique B sub-object of the D object pointed to by v. [... bla bla about cv1 and cv2 ...] and B shall be an accessible unambiguous base class of D (emphasis mine)

(recall 11.2 "A base class is said to be accessible if an invented public member of the base class is accessible.").

This explains why the first cast works. Now, for the second:

[...]

Otherwise, a run-time check is applied to see if the object pointed or referred to by v can be converted to the type pointed or referred to by T.

The run-time check logically executes as follows:

  • If, in the most derived object pointed (referred) to by v, v points (refers) to a public base class subobject of a T object, and if only one object of type T is derived from the sub-object pointed (referred) to by v, the result is a pointer (an lvalue referring) to that T object.
  • Otherwise, if v points (refers) to a public base class sub-object of the most derived object, and the type of the most derived object has a base class, of type T, that is unambiguous and public, the result is a pointer (an lvalue referring) to the T sub-object of the most derived object.
  • Otherwise, the run-time check fails.

The value of a failed cast to pointer type is the null pointer value of the required result type. A failed cast to reference type throws bad_cast (18.5.2).

So it seems that the behavior you observe is due to the private inheritance: even if the base class is accessible, it is not public, and the standard requires public, not accessible.

Annoying, isn't it ? I don't have the C++0x draft handy, perhaps someone can edit my answer with quotes from it, in case things have changed.

Is there another way to achieve this cast?

It depends on what you want to do. Basically, private inheritance is just another device for performing composition. If you really are to return a pointer to the private derived instance, then either make the inheritance public, or return a member.

Anyways, you'll happy to know that static_cast doesn't seem to have this limitation:

5.2.9. [about static_cast<T>(v)] [...]

An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

so if you know for sure what the actual dynamic type of the pointer is, you are allowed to static_cast inside foo.

I'd be interested in any additional information about why this inconsistency exists.

like image 159
Alexandre C. Avatar answered Oct 18 '22 14:10

Alexandre C.