Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is side-cast or cross-cast in Dynamic_cast in C++

What is side-cast/cross-cast in Dynamic_cast in C++. Can someone explain with an example?

#include <iostream>
using namespace std;

class A
{
    virtual void fn(){} 
};
class B:public A
{

};
class C:public A
{

};
int main() {

    B b;
    C* c;

    {
        c = dynamic_cast<C*> (&b);  
    }
    if(!c)
    {
        cout<<"invalid cast "<<endl;
    }
    return 0;
}

It prints invalid cast. So, what is side-cast?

like image 850
user1434287 Avatar asked Mar 12 '16 15:03

user1434287


People also ask

Which exception is thrown by dynamic_cast?

The bad_cast exception is thrown by the dynamic_cast operator as the result of a failed cast to a reference type.

What is the difference between dynamic_cast and Static_cast?

static_cast − This is used for the normal/ordinary type conversion. This is also the cast responsible for implicit type coersion and can also be called explicitly. You should use it in cases like converting float to int, char to int, etc. dynamic_cast −This cast is used for handling polymorphism.

What is dynamic cast in C?

Dynamic Cast: A cast is an operator that converts data from one type to another type. In C++, dynamic casting is mainly used for safe downcasting at run time. To work on dynamic_cast there must be one virtual function in the base class.

What is the use of dynamic_cast operator?

The primary purpose for the dynamic_cast operator is to perform type-safe downcasts. A downcast is the conversion of a pointer or reference to a class A to a pointer or reference to a class B , where class A is a base class of B .


1 Answers

The terms are pretty much explained in the comments, so I'm bundling that in my answer. But I think it is important to provide source code that shows the behavior of the code, since that provided by the asker only regards whether the crosscast compiles.

Assuming this inheritance tree (the dreaded diamond):

        BaseClass
       /         \
      V           V
   Left          Right
       \        /
        V      V
       MostDerived

Crosscast

A crosscast or sidecast is when dynamic_cast<Left*>(pRight) returns a Left* that properly behaves as a Left*. This happens when pRight points to a MostDerived object. Crosscasts only work with dynamic_cast, not reinterpret_cast or static_cast.

Downcasting and Upcasting

Downcast is casting from a Right* to a MostDerived*, which may fail. When it could fail, this should be done with a dynamic_cast, which sets the result to nullptr to signal failure. A common pattern is to have a dynamic_cast in debug, but to use a faster static_cast in the corresponding release code, as in:

#ifdef NDEBUG
return static_cast<MostDerived*>(input);
#else
auto p = dynamic_cast<MostDerived*>(input);
assert(p != nullptr && "Unable to cast input to MostDerived*");
return p; 
#endif

Upcasting is done implicitly when you pass a MostDerived* where any of the base classes is expected. This succeeds because MostDerived* is always a Right*, Left* and BaseClass*. However, because of the dreaded diamond, casting a MostDerived* to BaseClass* directly is ambiguous and results in a compiler error.

So, in order to get a BaseClass* from a MostDerived*, we must first cast to Left* or Right* through an implicit upcast. The resulting Left* or Right* can be cast to the other side with a crosscast by using dynamic_cast. If using static_cast, the behaviour of the resulting pointer is incorrect when casting to Right*. This is due to the order that they are specified in MostDerived. This is because static cast does not change the pointer address, and therefore does not use the new vtable to resolve function calls.

For this behaviour, it does not matter whether virtual inheritance is specified.

The term crosscast is used in The C++ Programming Language, Fourth Edition by Bjarne Stroustrup, while sidecast is mentioned in point 5b of cppreference's explanation on dynamic_cast.

#include <iostream>

struct BaseClass { virtual const char * behave() = 0; };
struct Left : BaseClass { virtual const char * behave() { return "Left"; } };
struct Right : BaseClass { virtual const char * behave() { return "Right"; } };
struct MostDerived : Left, Right { };

int main()
{
    MostDerived * mostDerived = new MostDerived();
    // implicit upcast through the diamond results in a compile-time error, ambiguous:
    // BaseClass * baseClass = mostDerived;
    Left * left = mostDerived;
    BaseClass * baseClassThroughLeft = left; // or, equivalently:
    // BaseClass * baseClassThroughLeft = reinterpret_cast<Left*>(mostDerived);
    Right * right = mostDerived;
    BaseClass * baseClassThroughRight = right;
    
    // this is of course ambiguous and does not compile
    //std::cout << mostDerived->behave() << std::endl;
    
    // note, only the right has a pointer value of 8 more
    // the two BaseClass pointers point to the same as left,
    // as does mostDerived
    std::cout << "left:  " << left << std::endl << "right: " << right << std::endl 
              << mostDerived << std::endl << baseClassThroughRight << std::endl 
              << baseClassThroughLeft << std::endl;

    std::cout << "Cast Left BaseClass * expression to Right *" << std::endl;
    std::cout << "with static_cast, behaves as " 
              << static_cast<Right *>(baseClassThroughLeft)->behave() 
              << " at addr: " << static_cast<Right *>(baseClassThroughLeft) << std::endl;
    std::cout << "with dynamic_cast, behaves as "
              << dynamic_cast<Right *>(baseClassThroughLeft)->behave() 
              << " at addr: " << dynamic_cast<Right *>(baseClassThroughLeft) << std::endl;
              
    std::cout << "Cast Right BaseClass * expression to Left *" << std::endl;
    std::cout << "with static_cast, behaves as " 
              << static_cast<Left *>(baseClassThroughRight)->behave() 
              << " at addr: " << static_cast<Left *>(baseClassThroughRight) << std::endl;
    std::cout << "with dynamic_cast, behaves as "
              << dynamic_cast<Left *>(baseClassThroughRight)->behave() 
              << " at addr: " << dynamic_cast<Left *>(baseClassThroughRight) << std::endl;

    delete mostDerived;
    return 0;
}

The output of the program is:

left:  0xeffeb0
right: 0xeffeb8
0xeffeb0
0xeffeb8
0xeffeb0
Cast Left BaseClass * expression to Right *
with static_cast, behaves as Left at addr: 0xeffeb0
with dynamic_cast, behaves as Right at addr: 0xeffeb8
Cast Right BaseClass * expression to Left *
with static_cast, behaves as Right at addr: 0xeffeb8
with dynamic_cast, behaves as Left at addr: 0xeffeb0
like image 108
TamaMcGlinn Avatar answered Oct 21 '22 09:10

TamaMcGlinn