Date: Fri, 29 Nov 2024 10:32:43 +0100
A note on my previous answer:
> Note that when you implicitly cast the iodevice* result from dynamic_cast
> to a odevice*, this applies a cast from a child to a base class, which
> always works
the part "always work" holds only in case the base class is pubilc.
If not public, maybe only old style cast works, but it would be a bad
practice.
And, sorry for the editing.
Il giorno ven 29 nov 2024 alle ore 10:26 mauro russo <
ing.russomauro_at_[hidden]> ha scritto:
>
> Hello, I am a user as you.
>
> > I'm reading the section about dynamic_cast, but I have some difficulties
> > understanding the rules, especially when the base class is private.
>
> > Here is the example code I'm dealing with
>
> > ----
> > #include <cassert>
>
> > struct device{virtual ~device() = default;};
> > struct idevice:device{};
> > struct odevice:device{};
> > struct iodevice:idevice,odevice{};
> > struct middle:iodevice{};
> > struct derived:private middle{idevice* as_idevice(){return this;}};
> > // or
> > // struct derived:private iodevice{idevice* as_idevice(){return this;}};
>
> > void test_device(idevice* id, bool leaf){
> > assert(id != nullptr);
> > odevice* iod = dynamic_cast<iodevice*>(id);
> > assert(iod != nullptr);
> > odevice* od = dynamic_cast<odevice*>(id);
> > if(leaf){
> > assert(od == nullptr);
> > }else{
> > assert(od != nullptr);
> > }
> > }
>
> >int main(){
> > auto d = derived();
> > test_device(d.as_idevice(), true);
> > auto m = middle();
> > test_device(&m, false);
> >}
> ----
>
> > It works consistently for gcc, clang and msvc, so I assume they are
> correct.
>
> > In expr.dynamic.cast, the standard says
>
> > ____
> > The result of the expression dynamic_cast<T>(v) [...]
>
>
> > If C is the class type to which T points or refers, the runtime check
> > logically executes as follows:
>
> > If, in the most derived object pointed (referred) to by v, v points
> > (refers) to a public base class subobject
> > of a C object, and if only one object of type C is derived from the
> > subobject pointed (referred) to by v
> > the result points (refers) to that C object.
>
> > Otherwise, if v points (refers) to a public base class subobject of the
> > most derived object, and the type
> > of the most derived object has a base class, of type C, that is
> > unambiguous and public, the result points
> > (refers) to the C subobject of the most derived object.
>
> > Otherwise, the runtime check fails.
> > ___
>
> In your case, when you start from an idevice* that refers
> to a base class of a "derived" (your class name) object:
> - the first step does not work because v does not point to a
> basic public class of the target type C = odevice,
> which means you are not down-casting.
> - the second step, related to side-casting, also does
> not work because odevice is not a public base class
> of the most derived object class "derived"
> Second step works for a "middle"(your class name) object, as odevice is
> a base public class of "middle", but it is a private base class of
> "derived".
>
> A further comment:
> Note that the reference to the most derived object even
> in the first step, is needed in case the most derived object
> contains two sub-objects of the target type C,
> that both virtually inherit from the class type to which v points.
> e.g.:
> class A{};
> class B : virtual public A {};
> class C : public B {};
> class D : public B {};
> class E : public C, public D {};
>
> In this case any object E contains two sub-objects B,
> but a single sub-object A,
> and if you try to dynamically cast from an A* to a B*
> when the most derived object is a E object, then
> you cannot chose what object B is the target and
> the cast fails, despite it is a relation from base to
> child class throuh public inheritance.
>
> > Since C does not appear in dynamic_cast<T>(v), and T is a type (thus it
> > does not point or refer...), should it maybe be "If C is the class type
> > to which v points or refers"?
>
> The sentence means that for example T may be C* or C& .
> If you use 'v' instead of 'T', then you will push for 'v' the
> constraint to have 'v' already pointing/referring to the
> target type, which means you don't need a cast anymore.
>
> > I fail to see why through idevice* a cast to iodevice* always works, but
> > a cast to odevice* not.
>
> The relation between idevice and iodevice is from base to child, that
> means the first step applies, where it is not required for the target C
> (i.e., iodevice) to be a public base class of the most derived object.
> Note that when you implicitly cast the iodevice* result from dynamic_cast
> to a odevice*, this applies a cast from a child to a base class, which
> always works (even dynamic_cast would apply it without requiring
> to deal with polymorphic types).
>
> > Both iodevice* and odevice* are not base classes of idevice*, but
> > iodevice* is a subclass of idevice*.
>
> > And all those relations are private to derived, but not to idevice and
> > iodevice.
>
> Hope the comments above clarified.
>
> > What also seems inconsistent, is that it is still possible to always get
> > a odevice* (since iodevice* is a public subclass of odevice*) but one
> > needs to do two conversions:
>
> get from what ?
>
> > ----
> > idevice* id = /**/;
> > assert(id != nullptr);
> > odevice* iod = dynamic_cast<iodevice*>(id);
> > assert(iod != nullptr);
> > odevice* od1 = dynamic_cast<iodevice*>(iod);
>
> Here the result depends on what is the expression used
> to initialize iddevice* id, as it might or not might point
> to any most derived object.
>
> > // or even without cast
> > odevice* od1 = iod;
> > assert(od1 != nullptr);
> > ----
>
> As explained above, this is from child to base.
>
> Note that when you implicitly cast the iodevice* result from dynamic_cast
> to a odevice*, this applies a cast from a child to a base class, which
> always works
the part "always work" holds only in case the base class is pubilc.
If not public, maybe only old style cast works, but it would be a bad
practice.
And, sorry for the editing.
Il giorno ven 29 nov 2024 alle ore 10:26 mauro russo <
ing.russomauro_at_[hidden]> ha scritto:
>
> Hello, I am a user as you.
>
> > I'm reading the section about dynamic_cast, but I have some difficulties
> > understanding the rules, especially when the base class is private.
>
> > Here is the example code I'm dealing with
>
> > ----
> > #include <cassert>
>
> > struct device{virtual ~device() = default;};
> > struct idevice:device{};
> > struct odevice:device{};
> > struct iodevice:idevice,odevice{};
> > struct middle:iodevice{};
> > struct derived:private middle{idevice* as_idevice(){return this;}};
> > // or
> > // struct derived:private iodevice{idevice* as_idevice(){return this;}};
>
> > void test_device(idevice* id, bool leaf){
> > assert(id != nullptr);
> > odevice* iod = dynamic_cast<iodevice*>(id);
> > assert(iod != nullptr);
> > odevice* od = dynamic_cast<odevice*>(id);
> > if(leaf){
> > assert(od == nullptr);
> > }else{
> > assert(od != nullptr);
> > }
> > }
>
> >int main(){
> > auto d = derived();
> > test_device(d.as_idevice(), true);
> > auto m = middle();
> > test_device(&m, false);
> >}
> ----
>
> > It works consistently for gcc, clang and msvc, so I assume they are
> correct.
>
> > In expr.dynamic.cast, the standard says
>
> > ____
> > The result of the expression dynamic_cast<T>(v) [...]
>
>
> > If C is the class type to which T points or refers, the runtime check
> > logically executes as follows:
>
> > If, in the most derived object pointed (referred) to by v, v points
> > (refers) to a public base class subobject
> > of a C object, and if only one object of type C is derived from the
> > subobject pointed (referred) to by v
> > the result points (refers) to that C object.
>
> > Otherwise, if v points (refers) to a public base class subobject of the
> > most derived object, and the type
> > of the most derived object has a base class, of type C, that is
> > unambiguous and public, the result points
> > (refers) to the C subobject of the most derived object.
>
> > Otherwise, the runtime check fails.
> > ___
>
> In your case, when you start from an idevice* that refers
> to a base class of a "derived" (your class name) object:
> - the first step does not work because v does not point to a
> basic public class of the target type C = odevice,
> which means you are not down-casting.
> - the second step, related to side-casting, also does
> not work because odevice is not a public base class
> of the most derived object class "derived"
> Second step works for a "middle"(your class name) object, as odevice is
> a base public class of "middle", but it is a private base class of
> "derived".
>
> A further comment:
> Note that the reference to the most derived object even
> in the first step, is needed in case the most derived object
> contains two sub-objects of the target type C,
> that both virtually inherit from the class type to which v points.
> e.g.:
> class A{};
> class B : virtual public A {};
> class C : public B {};
> class D : public B {};
> class E : public C, public D {};
>
> In this case any object E contains two sub-objects B,
> but a single sub-object A,
> and if you try to dynamically cast from an A* to a B*
> when the most derived object is a E object, then
> you cannot chose what object B is the target and
> the cast fails, despite it is a relation from base to
> child class throuh public inheritance.
>
> > Since C does not appear in dynamic_cast<T>(v), and T is a type (thus it
> > does not point or refer...), should it maybe be "If C is the class type
> > to which v points or refers"?
>
> The sentence means that for example T may be C* or C& .
> If you use 'v' instead of 'T', then you will push for 'v' the
> constraint to have 'v' already pointing/referring to the
> target type, which means you don't need a cast anymore.
>
> > I fail to see why through idevice* a cast to iodevice* always works, but
> > a cast to odevice* not.
>
> The relation between idevice and iodevice is from base to child, that
> means the first step applies, where it is not required for the target C
> (i.e., iodevice) to be a public base class of the most derived object.
> Note that when you implicitly cast the iodevice* result from dynamic_cast
> to a odevice*, this applies a cast from a child to a base class, which
> always works (even dynamic_cast would apply it without requiring
> to deal with polymorphic types).
>
> > Both iodevice* and odevice* are not base classes of idevice*, but
> > iodevice* is a subclass of idevice*.
>
> > And all those relations are private to derived, but not to idevice and
> > iodevice.
>
> Hope the comments above clarified.
>
> > What also seems inconsistent, is that it is still possible to always get
> > a odevice* (since iodevice* is a public subclass of odevice*) but one
> > needs to do two conversions:
>
> get from what ?
>
> > ----
> > idevice* id = /**/;
> > assert(id != nullptr);
> > odevice* iod = dynamic_cast<iodevice*>(id);
> > assert(iod != nullptr);
> > odevice* od1 = dynamic_cast<iodevice*>(iod);
>
> Here the result depends on what is the expression used
> to initialize iddevice* id, as it might or not might point
> to any most derived object.
>
> > // or even without cast
> > odevice* od1 = iod;
> > assert(od1 != nullptr);
> > ----
>
> As explained above, this is from child to base.
>
Received on 2024-11-29 09:32:55