On Mon, Jul 8, 2024 at 1:55 PM Phil Endecott <std_proposals_list@chezphil.org> wrote:

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