In C++, why does casting to reference of derived type work?

2.6k Views Asked by At

Precisely, why does B b = (B&) a compile and work whereas B b = (B) a does not in the below program?

#include <iostream>
using namespace std;

class A {public: void f(){ cout<<"A"<<endl;} };

class B : public A { public: void f(){cout<<"B"<<endl;} };

void g(A a){  B b = (B&) a; b.f(); }

int main() {
    B b; g(b);
    return 0;
}

Is there something about casting to a derived type with reference that I am missing here ? If I just cast to B, it gives a compile time error that the constructor B(A a) does not exist.

3

There are 3 best solutions below

8
On BEST ANSWER

Because the implicit conversion from A to B doesn't exist, and you didn't define an explicit one either.

Reference casting, on the other hand, is valid because it is allowed for inherited types. More precisely, you can cast both ways between different classes in the same inheritance hierarchy. Same goes for pointers. The related concept is called polymorphism, if you'd like some pointers for further study.

Do note, however, that it only makes sense for an object that is of type Bto be cast to B. E. g.:

B b;
A& aRef = B; // equivalent of A& ref = (A&)B;
B& bRef = (B&)aRef;

What you did will fail at runtime as soon as you try to access some data or method of B that does not exist in A. Because your actual object is A, not B.

Upcasting (from an descendant to an ascendant) is always safe because any object of a class that inherits the base class is a valid base object. The downcasting, however, is dangerous for the exact reason I explained above, and should never be done using a C-style cast. Instead, use dynamic_cast:

B b;
A& aRef = B;
B& bRef = dynamic_cast<B&>(aRef);

dynamic_cast uses RTTI (run-time type information) to validate the operation and will throw an std::bad_cast exception if the conversion is not valid. This is unlike dynamic_casting pointers, in which case the cast returns nullptr instead of throwing an exception.

2
On

Class A has private/public members. Class B is derived from Class A and may have added more private/public members.

Class B "is a" derivative of Class A. However Class A "is not a" derivative of Class B. (IE: You can downcast A->B but not upcast B-A.)

The reason is that while B is a kind of A, A is not a kind of B so B's methods/members will not be present (even if a method has the same name in the source code it will not be the same method compiled due to name being obscured by the compiler).

3
On

B b = (B) a won't work because no conversion (constructor or conversion operator) was defined. B b = (B&) a works because it cast a to a reference to B (downcast by a static_cast), then call copy constructor of B. However in this case, a is not an actual object of B so this is undefined behavior. See [expr.static.cast] in C++ standard

If the object of type “cv1 B” is actually a subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined.

and [expr.cast] in C++ standard or http://en.cppreference.com/w/cpp/language/explicit_cast and http://en.cppreference.com/w/cpp/language/cast_operator