C++ Logo

std-discussion

Advanced search

Rules for dynamic_cast and private inheritance

From: Federico Kircheis <federico_at_[hidden]>
Date: Fri, 29 Nov 2024 09:27:31 +0100
Hello,

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.
___
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"?
I fail to see why through idevice* a cast to iodevice* always works, but 
a cast to odevice* not.
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.
What also seems inconsistent, it 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:
----
idevice* id = /**/;
assert(id != nullptr);
odevice* iod = dynamic_cast<iodevice*>(id);
assert(iod != nullptr);
odevice* od1 = dynamic_cast<iodevice*>(iod);
// or even without cast
odevice* od1 = iod;
assert(od1 != nullptr);
----
Best
Federico

Received on 2024-11-29 08:27:37