How to use dynamic_cast to downcast correctly?
Clash Royale CLAN TAG#URR8PPP
up vote
6
down vote
favorite
I am being very confused about dynamic_cast
. Material from C++ Primer and cppreference(rule 5) can't help me understand. (cppreference is way much harder than the book and I read them both very carefully)
From C++ Primer 5th:dynamic_cast<type*>(e)
In all cases, the type of
e
must be either a class type that is publicly derived from the target type, apublic
base class of the target type, or the same as the target type. Ife
has one of these types, then the cast will succeed...
So here's how I understand the quoted text above:
(Base class have virtual functions)
dynamic_cast
succeeds if :
e
is a public inherited derived class fromtype
.e
is children. Upcast.e
is a base class oftype
?type
is children. Downcast.e
is same astype
. Sidecast?
Sample code:
#include <iostream>
using namespace std;
struct A
virtual void foo()
;
struct B : A
;
struct C : B
;
int main()
A* pa = new B;
if (C* pc = dynamic_cast<C*>(pa))
cout << "1"; //B is a base class of C
return 0;
I don't understand why this downcast would fail, I think it satisfies condition 2. and rule 5) (from cppreference).
If the book is wrong(damn once again), would someone elaborate rule 5) from cppreference? I can't fully understand what it say without examples...
c++
 |Â
show 8 more comments
up vote
6
down vote
favorite
I am being very confused about dynamic_cast
. Material from C++ Primer and cppreference(rule 5) can't help me understand. (cppreference is way much harder than the book and I read them both very carefully)
From C++ Primer 5th:dynamic_cast<type*>(e)
In all cases, the type of
e
must be either a class type that is publicly derived from the target type, apublic
base class of the target type, or the same as the target type. Ife
has one of these types, then the cast will succeed...
So here's how I understand the quoted text above:
(Base class have virtual functions)
dynamic_cast
succeeds if :
e
is a public inherited derived class fromtype
.e
is children. Upcast.e
is a base class oftype
?type
is children. Downcast.e
is same astype
. Sidecast?
Sample code:
#include <iostream>
using namespace std;
struct A
virtual void foo()
;
struct B : A
;
struct C : B
;
int main()
A* pa = new B;
if (C* pc = dynamic_cast<C*>(pa))
cout << "1"; //B is a base class of C
return 0;
I don't understand why this downcast would fail, I think it satisfies condition 2. and rule 5) (from cppreference).
If the book is wrong(damn once again), would someone elaborate rule 5) from cppreference? I can't fully understand what it say without examples...
c++
3
That quote is wrong.dynamic_cast
can cast across class hierarchy; the source and target types may be unrelated to each other (there may be a third class derived from both of them).
â Igor Tandetnik
4 hours ago
AB
is not aC
.
â François Andrieux
4 hours ago
When types are being discussed here, they're referring to the actual object being casted. Your actual object isB
. It is notC
. Basically, the object being dynamic casted has to have aC
"somewhere in it", in a manner of speaking. This is not true here. P.S. That quote is wrong also, true, what Igor said.
â Sam Varshavchik
4 hours ago
Ife
is a pointer to the same type astype
, the cast is simply a no-op.
â Igor Tandetnik
4 hours ago
1
The C++ Primer is simply being wrong (again). But what are your expectations from this cast? Do you hope aC
object will appear out of thin air so that the result of the cast could point to it?
â n.m.
4 hours ago
 |Â
show 8 more comments
up vote
6
down vote
favorite
up vote
6
down vote
favorite
I am being very confused about dynamic_cast
. Material from C++ Primer and cppreference(rule 5) can't help me understand. (cppreference is way much harder than the book and I read them both very carefully)
From C++ Primer 5th:dynamic_cast<type*>(e)
In all cases, the type of
e
must be either a class type that is publicly derived from the target type, apublic
base class of the target type, or the same as the target type. Ife
has one of these types, then the cast will succeed...
So here's how I understand the quoted text above:
(Base class have virtual functions)
dynamic_cast
succeeds if :
e
is a public inherited derived class fromtype
.e
is children. Upcast.e
is a base class oftype
?type
is children. Downcast.e
is same astype
. Sidecast?
Sample code:
#include <iostream>
using namespace std;
struct A
virtual void foo()
;
struct B : A
;
struct C : B
;
int main()
A* pa = new B;
if (C* pc = dynamic_cast<C*>(pa))
cout << "1"; //B is a base class of C
return 0;
I don't understand why this downcast would fail, I think it satisfies condition 2. and rule 5) (from cppreference).
If the book is wrong(damn once again), would someone elaborate rule 5) from cppreference? I can't fully understand what it say without examples...
c++
I am being very confused about dynamic_cast
. Material from C++ Primer and cppreference(rule 5) can't help me understand. (cppreference is way much harder than the book and I read them both very carefully)
From C++ Primer 5th:dynamic_cast<type*>(e)
In all cases, the type of
e
must be either a class type that is publicly derived from the target type, apublic
base class of the target type, or the same as the target type. Ife
has one of these types, then the cast will succeed...
So here's how I understand the quoted text above:
(Base class have virtual functions)
dynamic_cast
succeeds if :
e
is a public inherited derived class fromtype
.e
is children. Upcast.e
is a base class oftype
?type
is children. Downcast.e
is same astype
. Sidecast?
Sample code:
#include <iostream>
using namespace std;
struct A
virtual void foo()
;
struct B : A
;
struct C : B
;
int main()
A* pa = new B;
if (C* pc = dynamic_cast<C*>(pa))
cout << "1"; //B is a base class of C
return 0;
I don't understand why this downcast would fail, I think it satisfies condition 2. and rule 5) (from cppreference).
If the book is wrong(damn once again), would someone elaborate rule 5) from cppreference? I can't fully understand what it say without examples...
c++
c++
edited 4 hours ago
asked 4 hours ago
Rick
1,491724
1,491724
3
That quote is wrong.dynamic_cast
can cast across class hierarchy; the source and target types may be unrelated to each other (there may be a third class derived from both of them).
â Igor Tandetnik
4 hours ago
AB
is not aC
.
â François Andrieux
4 hours ago
When types are being discussed here, they're referring to the actual object being casted. Your actual object isB
. It is notC
. Basically, the object being dynamic casted has to have aC
"somewhere in it", in a manner of speaking. This is not true here. P.S. That quote is wrong also, true, what Igor said.
â Sam Varshavchik
4 hours ago
Ife
is a pointer to the same type astype
, the cast is simply a no-op.
â Igor Tandetnik
4 hours ago
1
The C++ Primer is simply being wrong (again). But what are your expectations from this cast? Do you hope aC
object will appear out of thin air so that the result of the cast could point to it?
â n.m.
4 hours ago
 |Â
show 8 more comments
3
That quote is wrong.dynamic_cast
can cast across class hierarchy; the source and target types may be unrelated to each other (there may be a third class derived from both of them).
â Igor Tandetnik
4 hours ago
AB
is not aC
.
â François Andrieux
4 hours ago
When types are being discussed here, they're referring to the actual object being casted. Your actual object isB
. It is notC
. Basically, the object being dynamic casted has to have aC
"somewhere in it", in a manner of speaking. This is not true here. P.S. That quote is wrong also, true, what Igor said.
â Sam Varshavchik
4 hours ago
Ife
is a pointer to the same type astype
, the cast is simply a no-op.
â Igor Tandetnik
4 hours ago
1
The C++ Primer is simply being wrong (again). But what are your expectations from this cast? Do you hope aC
object will appear out of thin air so that the result of the cast could point to it?
â n.m.
4 hours ago
3
3
That quote is wrong.
dynamic_cast
can cast across class hierarchy; the source and target types may be unrelated to each other (there may be a third class derived from both of them).â Igor Tandetnik
4 hours ago
That quote is wrong.
dynamic_cast
can cast across class hierarchy; the source and target types may be unrelated to each other (there may be a third class derived from both of them).â Igor Tandetnik
4 hours ago
A
B
is not a C
.â François Andrieux
4 hours ago
A
B
is not a C
.â François Andrieux
4 hours ago
When types are being discussed here, they're referring to the actual object being casted. Your actual object is
B
. It is not C
. Basically, the object being dynamic casted has to have a C
"somewhere in it", in a manner of speaking. This is not true here. P.S. That quote is wrong also, true, what Igor said.â Sam Varshavchik
4 hours ago
When types are being discussed here, they're referring to the actual object being casted. Your actual object is
B
. It is not C
. Basically, the object being dynamic casted has to have a C
"somewhere in it", in a manner of speaking. This is not true here. P.S. That quote is wrong also, true, what Igor said.â Sam Varshavchik
4 hours ago
If
e
is a pointer to the same type as type
, the cast is simply a no-op.â Igor Tandetnik
4 hours ago
If
e
is a pointer to the same type as type
, the cast is simply a no-op.â Igor Tandetnik
4 hours ago
1
1
The C++ Primer is simply being wrong (again). But what are your expectations from this cast? Do you hope a
C
object will appear out of thin air so that the result of the cast could point to it?â n.m.
4 hours ago
The C++ Primer is simply being wrong (again). But what are your expectations from this cast? Do you hope a
C
object will appear out of thin air so that the result of the cast could point to it?â n.m.
4 hours ago
 |Â
show 8 more comments
4 Answers
4
active
oldest
votes
up vote
3
down vote
The last part is that the dynamic type of the object must match.
Here, you have a B
pointed to by an A
pointer. You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to. So the cast fails.
dynamic cast doesn't create objects, it just lets you access objects that are already there. When you call new B
it creates a B
object with an A
subobject. It does not create a C
object.
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actualBase
type toDerived
type casting here and should not happen logically.
â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
add a comment |Â
up vote
3
down vote
Here is the rule from cppreference with my annotations:
5) If expression is a pointer or reference to a polymorphic type Base,
and new_type is a pointer or reference to the type Derived a run-time
check is performed:
This applies. B
is a base of C
.
a) The most derived object pointed/identified by
expression is examined. If, in that object, expression points/refers
to a public base of Derived, and if only one subobject of Derived type
is derived from the subobject pointed/identified by expression, then
the result of the cast points/refers to that Derived subobject. (This
is known as a "downcast".)
The most dervied object pointed by pa
is of type B
.
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance. The pointed B
instance is a "concrete" object. So, this case does not apply.
An example:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers
to a public base of the most derived object, and, simultaneously, the
most derived object has an unambiguous public base class of type
Derived, the result of the cast points/refers to that Derived (This is
known as a "sidecast".)
pa
does not point to a most derived object whose base class is C
, so this case does not apply.
An example of side cast:
struct base
virtual ~base(); // for polymorphism
;
struct left : base ;
struct right : base ;
struct derived : left, right ;
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails. If the
dynamic_cast is used on pointers, the null pointer value of type
new_type is returned. If it was used on references, the exception
std::bad_cast is thrown.
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does.
- e is same as type. Sidecast?
Not a sidecast. A sidecast is explained in 5b. Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
It may be that the conditions of the book attempt describe whether the conversion is well-formed. Although, "then the cast will succeed" certainly seems to imply more. The quoted rules are not correct for describing whether the cast succeeds at runtime.
If the entire program is well-formed, then a compiler must compile the program. If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
The example program that you've shown is well-formed and it must successfully compile. It does compile on my system.
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
add a comment |Â
up vote
1
down vote
The issue is that the statement A* pa = new B;
creates a B
.
But a B
doesn't contain a C
(downwards, upwards, or sideways), so the dynamic cast from pa
to a C*
will certainly fail.
add a comment |Â
up vote
0
down vote
I'm, for the most part, adding another example of a side cast to previous answersâ¦
struct A ;
struct B virtual ~B() = default; ;
struct C : A, B ;
A *side_cast()
B *obj = new C;
return dynamic_cast<A *>(obj);
The above is a legal "side cast", and does not return null. This shows that the target type neither:
- has to be polymorphic.
- has to be related to the static type of
*
expression.
for the cast to succeed at run time.
The cast is well-formed, regardless of whether the type of the most-derived object pointed to by expression (IOW, the dynamic type of *
expression) inherits from the target type. However, it will return static_cast<A *>(nullptr)
unless A
is a public and unambiguous base class of the dynamic type of *
expression. The gist of this is that you can legally write a whole bunch of nonsensical casts like dynamic_cast<std::tuple<int, float> *>(&std::cin)
â note that std::cin
is of type std::istream
which is polymorphic âÂÂ, but you'll simply get a null pointer at runtime.
Simply put, a dynamic_cast
between pointers can do most of the things that a static_cast
can (except, at least, non-polymorphic downcast*), and, when the static type of *
expression is polymorphic, it can also cast to any pointer to class whatsoever, except for removing cv-qualifers (const
or volatile
). However, whether the cast actually returns non-null depends on the specific condition, checked at run time, mentioned above.
* The reason why this is forbidden is that there is no safe way to do this, and dynamic_cast
is expected to be safe. Hence they make you write static_cast
to make it clear that any ensuing UB is your fault.
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
The last part is that the dynamic type of the object must match.
Here, you have a B
pointed to by an A
pointer. You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to. So the cast fails.
dynamic cast doesn't create objects, it just lets you access objects that are already there. When you call new B
it creates a B
object with an A
subobject. It does not create a C
object.
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actualBase
type toDerived
type casting here and should not happen logically.
â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
add a comment |Â
up vote
3
down vote
The last part is that the dynamic type of the object must match.
Here, you have a B
pointed to by an A
pointer. You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to. So the cast fails.
dynamic cast doesn't create objects, it just lets you access objects that are already there. When you call new B
it creates a B
object with an A
subobject. It does not create a C
object.
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actualBase
type toDerived
type casting here and should not happen logically.
â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
add a comment |Â
up vote
3
down vote
up vote
3
down vote
The last part is that the dynamic type of the object must match.
Here, you have a B
pointed to by an A
pointer. You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to. So the cast fails.
dynamic cast doesn't create objects, it just lets you access objects that are already there. When you call new B
it creates a B
object with an A
subobject. It does not create a C
object.
The last part is that the dynamic type of the object must match.
Here, you have a B
pointed to by an A
pointer. You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to. So the cast fails.
dynamic cast doesn't create objects, it just lets you access objects that are already there. When you call new B
it creates a B
object with an A
subobject. It does not create a C
object.
answered 4 hours ago
Yakk - Adam Nevraumont
171k18175353
171k18175353
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actualBase
type toDerived
type casting here and should not happen logically.
â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
add a comment |Â
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actualBase
type toDerived
type casting here and should not happen logically.
â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actual
Base
type to Derived
type casting here and should not happen logically.â Rick
3 hours ago
Hmmm. That makes sense. Then why it has something called downcast? I mean, there's no actual
Base
type to Derived
type casting here and should not happen logically.â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
So downcast mentioned by cppreference is merely saying that it is the pointer that is "downcasting"?
â Rick
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
@Rick The class heirarchy exists regardless. Typically we put the base classes above the derived classes when we draw a class heirarchy graph. Then "down" here refers to "to a location lower in the drawing" (usually with "oh, and with a line of descent connecting). "cross" cast is left-to-right where one isn't above another (no line of descent). And "up" cast goes up the graph towards the top.
â Yakk - Adam Nevraumont
3 hours ago
add a comment |Â
up vote
3
down vote
Here is the rule from cppreference with my annotations:
5) If expression is a pointer or reference to a polymorphic type Base,
and new_type is a pointer or reference to the type Derived a run-time
check is performed:
This applies. B
is a base of C
.
a) The most derived object pointed/identified by
expression is examined. If, in that object, expression points/refers
to a public base of Derived, and if only one subobject of Derived type
is derived from the subobject pointed/identified by expression, then
the result of the cast points/refers to that Derived subobject. (This
is known as a "downcast".)
The most dervied object pointed by pa
is of type B
.
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance. The pointed B
instance is a "concrete" object. So, this case does not apply.
An example:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers
to a public base of the most derived object, and, simultaneously, the
most derived object has an unambiguous public base class of type
Derived, the result of the cast points/refers to that Derived (This is
known as a "sidecast".)
pa
does not point to a most derived object whose base class is C
, so this case does not apply.
An example of side cast:
struct base
virtual ~base(); // for polymorphism
;
struct left : base ;
struct right : base ;
struct derived : left, right ;
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails. If the
dynamic_cast is used on pointers, the null pointer value of type
new_type is returned. If it was used on references, the exception
std::bad_cast is thrown.
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does.
- e is same as type. Sidecast?
Not a sidecast. A sidecast is explained in 5b. Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
It may be that the conditions of the book attempt describe whether the conversion is well-formed. Although, "then the cast will succeed" certainly seems to imply more. The quoted rules are not correct for describing whether the cast succeeds at runtime.
If the entire program is well-formed, then a compiler must compile the program. If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
The example program that you've shown is well-formed and it must successfully compile. It does compile on my system.
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
add a comment |Â
up vote
3
down vote
Here is the rule from cppreference with my annotations:
5) If expression is a pointer or reference to a polymorphic type Base,
and new_type is a pointer or reference to the type Derived a run-time
check is performed:
This applies. B
is a base of C
.
a) The most derived object pointed/identified by
expression is examined. If, in that object, expression points/refers
to a public base of Derived, and if only one subobject of Derived type
is derived from the subobject pointed/identified by expression, then
the result of the cast points/refers to that Derived subobject. (This
is known as a "downcast".)
The most dervied object pointed by pa
is of type B
.
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance. The pointed B
instance is a "concrete" object. So, this case does not apply.
An example:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers
to a public base of the most derived object, and, simultaneously, the
most derived object has an unambiguous public base class of type
Derived, the result of the cast points/refers to that Derived (This is
known as a "sidecast".)
pa
does not point to a most derived object whose base class is C
, so this case does not apply.
An example of side cast:
struct base
virtual ~base(); // for polymorphism
;
struct left : base ;
struct right : base ;
struct derived : left, right ;
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails. If the
dynamic_cast is used on pointers, the null pointer value of type
new_type is returned. If it was used on references, the exception
std::bad_cast is thrown.
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does.
- e is same as type. Sidecast?
Not a sidecast. A sidecast is explained in 5b. Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
It may be that the conditions of the book attempt describe whether the conversion is well-formed. Although, "then the cast will succeed" certainly seems to imply more. The quoted rules are not correct for describing whether the cast succeeds at runtime.
If the entire program is well-formed, then a compiler must compile the program. If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
The example program that you've shown is well-formed and it must successfully compile. It does compile on my system.
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
add a comment |Â
up vote
3
down vote
up vote
3
down vote
Here is the rule from cppreference with my annotations:
5) If expression is a pointer or reference to a polymorphic type Base,
and new_type is a pointer or reference to the type Derived a run-time
check is performed:
This applies. B
is a base of C
.
a) The most derived object pointed/identified by
expression is examined. If, in that object, expression points/refers
to a public base of Derived, and if only one subobject of Derived type
is derived from the subobject pointed/identified by expression, then
the result of the cast points/refers to that Derived subobject. (This
is known as a "downcast".)
The most dervied object pointed by pa
is of type B
.
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance. The pointed B
instance is a "concrete" object. So, this case does not apply.
An example:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers
to a public base of the most derived object, and, simultaneously, the
most derived object has an unambiguous public base class of type
Derived, the result of the cast points/refers to that Derived (This is
known as a "sidecast".)
pa
does not point to a most derived object whose base class is C
, so this case does not apply.
An example of side cast:
struct base
virtual ~base(); // for polymorphism
;
struct left : base ;
struct right : base ;
struct derived : left, right ;
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails. If the
dynamic_cast is used on pointers, the null pointer value of type
new_type is returned. If it was used on references, the exception
std::bad_cast is thrown.
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does.
- e is same as type. Sidecast?
Not a sidecast. A sidecast is explained in 5b. Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
It may be that the conditions of the book attempt describe whether the conversion is well-formed. Although, "then the cast will succeed" certainly seems to imply more. The quoted rules are not correct for describing whether the cast succeeds at runtime.
If the entire program is well-formed, then a compiler must compile the program. If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
The example program that you've shown is well-formed and it must successfully compile. It does compile on my system.
Here is the rule from cppreference with my annotations:
5) If expression is a pointer or reference to a polymorphic type Base,
and new_type is a pointer or reference to the type Derived a run-time
check is performed:
This applies. B
is a base of C
.
a) The most derived object pointed/identified by
expression is examined. If, in that object, expression points/refers
to a public base of Derived, and if only one subobject of Derived type
is derived from the subobject pointed/identified by expression, then
the result of the cast points/refers to that Derived subobject. (This
is known as a "downcast".)
The most dervied object pointed by pa
is of type B
.
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance. The pointed B
instance is a "concrete" object. So, this case does not apply.
An example:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers
to a public base of the most derived object, and, simultaneously, the
most derived object has an unambiguous public base class of type
Derived, the result of the cast points/refers to that Derived (This is
known as a "sidecast".)
pa
does not point to a most derived object whose base class is C
, so this case does not apply.
An example of side cast:
struct base
virtual ~base(); // for polymorphism
;
struct left : base ;
struct right : base ;
struct derived : left, right ;
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails. If the
dynamic_cast is used on pointers, the null pointer value of type
new_type is returned. If it was used on references, the exception
std::bad_cast is thrown.
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does.
- e is same as type. Sidecast?
Not a sidecast. A sidecast is explained in 5b. Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
It may be that the conditions of the book attempt describe whether the conversion is well-formed. Although, "then the cast will succeed" certainly seems to imply more. The quoted rules are not correct for describing whether the cast succeeds at runtime.
If the entire program is well-formed, then a compiler must compile the program. If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
The example program that you've shown is well-formed and it must successfully compile. It does compile on my system.
edited 1 hour ago
answered 3 hours ago
user2079303
69.5k550109
69.5k550109
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
add a comment |Â
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
I read that rule very carefully and know it fails. The point is that I don't understand what a) and b) are talking about at all. My understanding are mostly based on guessing the meaning of a) and b).
â Rick
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
@Rick I added explanations.
â user2079303
3 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
Nice answer, but just to be clear: In a "side cast", only the source type must be polymorphic. The (static) target type just has to be an unambiguous public base class of the most derived class, whether it's polymorphic or not.
â Arne Vogel
2 hours ago
add a comment |Â
up vote
1
down vote
The issue is that the statement A* pa = new B;
creates a B
.
But a B
doesn't contain a C
(downwards, upwards, or sideways), so the dynamic cast from pa
to a C*
will certainly fail.
add a comment |Â
up vote
1
down vote
The issue is that the statement A* pa = new B;
creates a B
.
But a B
doesn't contain a C
(downwards, upwards, or sideways), so the dynamic cast from pa
to a C*
will certainly fail.
add a comment |Â
up vote
1
down vote
up vote
1
down vote
The issue is that the statement A* pa = new B;
creates a B
.
But a B
doesn't contain a C
(downwards, upwards, or sideways), so the dynamic cast from pa
to a C*
will certainly fail.
The issue is that the statement A* pa = new B;
creates a B
.
But a B
doesn't contain a C
(downwards, upwards, or sideways), so the dynamic cast from pa
to a C*
will certainly fail.
answered 3 hours ago
Bathsheba
169k26239355
169k26239355
add a comment |Â
add a comment |Â
up vote
0
down vote
I'm, for the most part, adding another example of a side cast to previous answersâ¦
struct A ;
struct B virtual ~B() = default; ;
struct C : A, B ;
A *side_cast()
B *obj = new C;
return dynamic_cast<A *>(obj);
The above is a legal "side cast", and does not return null. This shows that the target type neither:
- has to be polymorphic.
- has to be related to the static type of
*
expression.
for the cast to succeed at run time.
The cast is well-formed, regardless of whether the type of the most-derived object pointed to by expression (IOW, the dynamic type of *
expression) inherits from the target type. However, it will return static_cast<A *>(nullptr)
unless A
is a public and unambiguous base class of the dynamic type of *
expression. The gist of this is that you can legally write a whole bunch of nonsensical casts like dynamic_cast<std::tuple<int, float> *>(&std::cin)
â note that std::cin
is of type std::istream
which is polymorphic âÂÂ, but you'll simply get a null pointer at runtime.
Simply put, a dynamic_cast
between pointers can do most of the things that a static_cast
can (except, at least, non-polymorphic downcast*), and, when the static type of *
expression is polymorphic, it can also cast to any pointer to class whatsoever, except for removing cv-qualifers (const
or volatile
). However, whether the cast actually returns non-null depends on the specific condition, checked at run time, mentioned above.
* The reason why this is forbidden is that there is no safe way to do this, and dynamic_cast
is expected to be safe. Hence they make you write static_cast
to make it clear that any ensuing UB is your fault.
add a comment |Â
up vote
0
down vote
I'm, for the most part, adding another example of a side cast to previous answersâ¦
struct A ;
struct B virtual ~B() = default; ;
struct C : A, B ;
A *side_cast()
B *obj = new C;
return dynamic_cast<A *>(obj);
The above is a legal "side cast", and does not return null. This shows that the target type neither:
- has to be polymorphic.
- has to be related to the static type of
*
expression.
for the cast to succeed at run time.
The cast is well-formed, regardless of whether the type of the most-derived object pointed to by expression (IOW, the dynamic type of *
expression) inherits from the target type. However, it will return static_cast<A *>(nullptr)
unless A
is a public and unambiguous base class of the dynamic type of *
expression. The gist of this is that you can legally write a whole bunch of nonsensical casts like dynamic_cast<std::tuple<int, float> *>(&std::cin)
â note that std::cin
is of type std::istream
which is polymorphic âÂÂ, but you'll simply get a null pointer at runtime.
Simply put, a dynamic_cast
between pointers can do most of the things that a static_cast
can (except, at least, non-polymorphic downcast*), and, when the static type of *
expression is polymorphic, it can also cast to any pointer to class whatsoever, except for removing cv-qualifers (const
or volatile
). However, whether the cast actually returns non-null depends on the specific condition, checked at run time, mentioned above.
* The reason why this is forbidden is that there is no safe way to do this, and dynamic_cast
is expected to be safe. Hence they make you write static_cast
to make it clear that any ensuing UB is your fault.
add a comment |Â
up vote
0
down vote
up vote
0
down vote
I'm, for the most part, adding another example of a side cast to previous answersâ¦
struct A ;
struct B virtual ~B() = default; ;
struct C : A, B ;
A *side_cast()
B *obj = new C;
return dynamic_cast<A *>(obj);
The above is a legal "side cast", and does not return null. This shows that the target type neither:
- has to be polymorphic.
- has to be related to the static type of
*
expression.
for the cast to succeed at run time.
The cast is well-formed, regardless of whether the type of the most-derived object pointed to by expression (IOW, the dynamic type of *
expression) inherits from the target type. However, it will return static_cast<A *>(nullptr)
unless A
is a public and unambiguous base class of the dynamic type of *
expression. The gist of this is that you can legally write a whole bunch of nonsensical casts like dynamic_cast<std::tuple<int, float> *>(&std::cin)
â note that std::cin
is of type std::istream
which is polymorphic âÂÂ, but you'll simply get a null pointer at runtime.
Simply put, a dynamic_cast
between pointers can do most of the things that a static_cast
can (except, at least, non-polymorphic downcast*), and, when the static type of *
expression is polymorphic, it can also cast to any pointer to class whatsoever, except for removing cv-qualifers (const
or volatile
). However, whether the cast actually returns non-null depends on the specific condition, checked at run time, mentioned above.
* The reason why this is forbidden is that there is no safe way to do this, and dynamic_cast
is expected to be safe. Hence they make you write static_cast
to make it clear that any ensuing UB is your fault.
I'm, for the most part, adding another example of a side cast to previous answersâ¦
struct A ;
struct B virtual ~B() = default; ;
struct C : A, B ;
A *side_cast()
B *obj = new C;
return dynamic_cast<A *>(obj);
The above is a legal "side cast", and does not return null. This shows that the target type neither:
- has to be polymorphic.
- has to be related to the static type of
*
expression.
for the cast to succeed at run time.
The cast is well-formed, regardless of whether the type of the most-derived object pointed to by expression (IOW, the dynamic type of *
expression) inherits from the target type. However, it will return static_cast<A *>(nullptr)
unless A
is a public and unambiguous base class of the dynamic type of *
expression. The gist of this is that you can legally write a whole bunch of nonsensical casts like dynamic_cast<std::tuple<int, float> *>(&std::cin)
â note that std::cin
is of type std::istream
which is polymorphic âÂÂ, but you'll simply get a null pointer at runtime.
Simply put, a dynamic_cast
between pointers can do most of the things that a static_cast
can (except, at least, non-polymorphic downcast*), and, when the static type of *
expression is polymorphic, it can also cast to any pointer to class whatsoever, except for removing cv-qualifers (const
or volatile
). However, whether the cast actually returns non-null depends on the specific condition, checked at run time, mentioned above.
* The reason why this is forbidden is that there is no safe way to do this, and dynamic_cast
is expected to be safe. Hence they make you write static_cast
to make it clear that any ensuing UB is your fault.
edited 1 hour ago
answered 1 hour ago
Arne Vogel
3,29711023
3,29711023
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52556957%2fhow-to-use-dynamic-cast-to-downcast-correctly%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
3
That quote is wrong.
dynamic_cast
can cast across class hierarchy; the source and target types may be unrelated to each other (there may be a third class derived from both of them).â Igor Tandetnik
4 hours ago
A
B
is not aC
.â François Andrieux
4 hours ago
When types are being discussed here, they're referring to the actual object being casted. Your actual object is
B
. It is notC
. Basically, the object being dynamic casted has to have aC
"somewhere in it", in a manner of speaking. This is not true here. P.S. That quote is wrong also, true, what Igor said.â Sam Varshavchik
4 hours ago
If
e
is a pointer to the same type astype
, the cast is simply a no-op.â Igor Tandetnik
4 hours ago
1
The C++ Primer is simply being wrong (again). But what are your expectations from this cast? Do you hope a
C
object will appear out of thin air so that the result of the cast could point to it?â n.m.
4 hours ago