Can you make it work if there is no common base class? I.e.
struct LeftCat { virtual ~LeftCat() = default; };
struct RightCat { virtual ~RightCat() = default; };
struct SiameseCat: LeftCat, RightCat {};
int main() {
SiameseCat sc;
auto lc = static_cast<LeftCat*>(&sc); assert(lc);
That's an upcast, not a downcast.
auto rc = down_cast<RightCat*>(&sc); assert(rc);
That's an upcast, not a downcast.
auto lrc = down_cast<RightCat*>(lc); assert(!lrc); // dynamic_cast will sidecast here.
That's a sidecast, not a downcast.
The only ways "down" the hierarchy I presented are (Cat => LeftCat), (Cat => RightCat), (LeftCat => SiameseCat), and (RightCat => SiameseCat).
You can exclude non-down-casts with a plain old static_assert:
template<class To, class From>
To *downcast_to_leftcat(From *c) {
static_assert(std::is_base_of_v<From, To>, "Hey! I only do downcasts");
if (To *lc = dynamic_cast<To*>(c)) {
if (static_cast<From*>(lc) == c) {
return lc; // this was definitely a pure downcast
}
}
return nullptr;
}
–Arthur