3

I was going through the, "Multiple Inheritance for C++ by Bjarne Stroustrup, Published in the May 1999 issue of "The C/C++ Users Journal"". The below excerpt is from the same (Page 5/17),

4.4 Casting

Explicit and implicit casting may also involve modifying a pointer value with a delta:

class A { void f(); };
class B { int f(); };
class C : A, B { };

C* pc;
B* pb; 
pb = (B*)pc; // pb = (B*)((char*)pc+delta(B)) 
pb = pc; // pb = (B*)((char*)pc+delta(B)) 
pc = pb; // error: cast needed <-------------------- HERE
pc = (C*)pb; // pc = (C*)((char*)pb-delta(B))

He shows us that pb = pc can be done without casting it explicitly. That definitely means that the casting is handled implicitly. Then,

  1. why, when we try to pc = pb pointer, is casting necessary?
  2. What and where is this rule that directs this?
  3. Is it related to the incrementing/decrementing the pointer by delta value?

EDIT

Jonathan Mee marked this question as the duplicate of "What Type of Cast to Go from Parent to Child?". I am afraid, I don't concur. My question is regarding, why casting and where is this rule that directs us to cast or not to cast. I think the logic behind might be same but the concept is entirely different. In his question, he is doubtful on (insists on not using dynamic cast) the use of dynamic_cast and static_cast. My doubt is still some steps behind his.

Community
  • 1
  • 1
Abhineet
  • 5,320
  • 1
  • 25
  • 43
  • You'll have a good explanation here: http://stackoverflow.com/questions/25137705/c-memory-layout-of-classes-using-inheritance – Dfaure Jul 13 '16 at 12:21
  • Possible duplicate of [What Type of Cast to Go from Parent to Child?](http://stackoverflow.com/questions/32506579/what-type-of-cast-to-go-from-parent-to-child) – Jonathan Mee Jul 13 '16 at 12:23
  • @JonathanMee - My question is regarding, why casting and where is this rule that directs us to cast or not to cast. I think the logic behind might be same but the concept is entirely different. My doubt vs your doubt don't have any similarity to be marked as duplicate. In your question, you know why you are casting but you have a doubt as to which one to use. My doubt is still some steps behind yours :) – Abhineet Jul 13 '16 at 12:30

4 Answers4

4

It has to do with the types we know that the objects have. Every C is also a B, so the conversion always works (implicitly).

Every B is not part of a C, so you have to tell the compiler that you know for certain that the this particular conversion will work. That's the use of an explicit cast.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Perhaps this is out of scope, but what is the behavior when a cast like that (parent -> child) is not valid? – rtmh Jul 13 '16 at 13:05
  • Sweet and simple. I loved the "part" part of the answer. And that definitely clears lots of air around the implicit and explicit cast. – Abhineet Jul 14 '16 at 05:16
3

The rules on allowed implicit conversions for non-fundamental types are as follows:

  • Null pointers can be converted to pointers of any type
  • Pointers to any type can be converted to void pointers.
  • Pointer upcast: pointers to a derived class can be converted to a pointer of an accessible and unambiguous base class, without modifying its const or volatile qualification.

So when upcasting from a C* to a B* an explicit cast is not necessary. But because casting from a B* to a C* is a pointer downcast a C-Style cast may be used, but one of the following is preferred:

  • dynamic_cast can be used given that expression is a B* and new_type is a C* this run-time check will be performed and the downcast should be allowed by 1:
  1. The most derived object pointed/identified by expression is examined. If, in that object, expression points/refers to a public base of Derived, and if only one subobject of Derived type is derived from the subobject pointed/identified by expression, then the result of the cast points/refers to that Derived subobject. (This is known as a "downcast".)
  2. Otherwise, if expression points/refers to a public base of the most derived object, and, simultaneously, the most derived object has an unambiguous public base class of type Derived, the result of the cast points/refers to that Derived (This is known as a "sidecast".)
  3. Otherwise, the runtime check fails. If the dynamic_cast is used on pointers, the null pointer value of type new_type is returned.
  • reinterpret_cast can be used, and will always succeed. But the use of it's result is only defined if, given B is AliasedType and C is DynamicType, one of these conditions is met, the 2nd bullet allows the downcast:
  • AliasedType is (possibly cv-qualified) DynamicType
  • AliasedType and DynamicType are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T
  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType
  • AliasedType is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to obtain a usable pointer to a struct or union given a pointer to its non-static member or element.
  • AliasedType is a (possibly cv-qualified) base class of DynamicType
  • AliasedType is char or unsigned char: this permits examination of the object representation of any object as an array of unsigned char.
  • static_cast can also be used if and only if it is known that the B* is actually polymorphically addressing a C object. If this is not the case and a static_cast is attempted, undefined behavior will result.
Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
1

Well, the compiler knows that a pointer to C can be referenced just as the inherited class A or B - so casting up to A or B can be done implicitly. Going the other way the compiler doesn't know the pointer is to an object of C. It could be a different class that also inherits A. By adding a cast you are essentially saying "don't worry compiler - I know it's actually a class of type C".

noelicus
  • 14,468
  • 3
  • 92
  • 111
1

In your case it is pretty simple. You attempt to assign a pointer to an object of class B to a pointer which points to objects of class C.

Per se, you cannot do that, because objects of class C also provide the functionality of class A, which objects of class B do not. Thus, the use of an object of class B where an object C is expected is undefined, because B cannot provide everything C has.

Jonas Schäfer
  • 20,140
  • 5
  • 55
  • 69