On Tue, Jul 9, 2024 at 10:53 AM Phil Endecott <std_proposals_list@chezphil.org> wrote:
Arthur O'Dwyer wrote:
> That's an upcast, not a downcast.

Sorry for my confused post yesterday. I got muddled while trying to map your
cats onto my already-half-forgotten original problem.

Here is a longer example that is closer to the code I was originally struggling with:
[...]
struct V { virtual ~V() = default; };
struct D { void foo() { std::cout << "foo"; } };

D here isn't a polymorphic type at all, so you can't use `dynamic_cast` with it. (Except in the special case [expr.dynamic.cast]/4 where it's just a wacky spelling for `static_cast`).
You can check for that situation with `std::is_polymorphic_v`, and/or by using the `downcast` template I posted abovethread, which explicitly tests that we're doing a downcast.
https://godbolt.org/z/1Psq57acn

  foo("inherited CV", static_cast<CV&>(inherited));  // Unexpectedly calls D::foo.
  foo("inherited DV", static_cast<DV&>(inherited));  // Calls D::foo.

FWIW, this example was initially confusing because you said "unexpectedly calls D::foo," and there's literally only one `foo` in the whole program (which is of course `D::foo`).
If you wanted some other `foo` to be called, wouldn't you have to provide it?
But then I realized that you meant "D::foo is called because dynamic_cast unexpectedly returns non-null,"
and that's because `dynamic_cast` unexpectedly doesn't behave like `downcast`,
and that's because... you didn't write `downcast`.
But if you do write `downcast`, I think everything works fine, right?


The consequence is that a class designer needs to consider how his class might
be inherited from (or, make it 'final').

Well, yes. Almost every class (certainly every business-logic class) is either:
- a non-polymorphic value-semantic type,
- a polymorphic abstract base class ("interface"), or
- a polymorphic leaf class ("implementation").
The type author should always know which of those things he's writing.
Only base classes should ever be inherited-from. You can enforce this (with `final`), although in practice usually I think that's overkill.

That's mostly irrelevant to this thread. But what is relevant is that `dynamic_cast` should be used only with polymorphic types!

–Arthur