C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Downcasting without Sidecasting

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Wed, 10 Jul 2024 12:53:07 -0400
On Tue, Jul 9, 2024 at 10:53 AM Phil Endecott <
std_proposals_list_at_[hidden]> 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
<https://timsong-cpp.github.io/cppwp/n4868/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

Received on 2024-07-10 16:53:21