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