How to use dynamic_cast to downcast correctly?

The name of the pictureThe name of the pictureThe name of the pictureClash 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, a public base class of the target type, or the same as the target type. If e 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 :




  1. e is a public inherited derived class from type. e is children. Upcast.


  2. e is a base class of type? type is children. Downcast.


  3. e is same as type. 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...










share|improve this question



















  • 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 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











  • If e is a pointer to the same type as type, 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















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, a public base class of the target type, or the same as the target type. If e 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 :




  1. e is a public inherited derived class from type. e is children. Upcast.


  2. e is a base class of type? type is children. Downcast.


  3. e is same as type. 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...










share|improve this question



















  • 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 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











  • If e is a pointer to the same type as type, 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













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, a public base class of the target type, or the same as the target type. If e 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 :




  1. e is a public inherited derived class from type. e is children. Upcast.


  2. e is a base class of type? type is children. Downcast.


  3. e is same as type. 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...










share|improve this question















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, a public base class of the target type, or the same as the target type. If e 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 :




  1. e is a public inherited derived class from type. e is children. Upcast.


  2. e is a base class of type? type is children. Downcast.


  3. e is same as type. 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++






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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










  • 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











  • If e is a pointer to the same type as type, 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













  • 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 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











  • If e is a pointer to the same type as type, 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








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













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.






share|improve this answer




















  • 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










  • @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

















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.





  1. 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.






share|improve this answer






















  • 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


















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.






share|improve this answer



























    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.






    share|improve this answer






















      Your Answer





      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: false,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













       

      draft saved


      draft discarded


















      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






























      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.






      share|improve this answer




















      • 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










      • @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














      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.






      share|improve this answer




















      • 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










      • @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












      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.






      share|improve this answer












      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.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      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 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










      • @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










      • 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












      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.





      1. 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.






      share|improve this answer






















      • 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















      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.





      1. 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.






      share|improve this answer






















      • 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













      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.





      1. 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.






      share|improve this answer














      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.





      1. 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.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      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

















      • 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











      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.






      share|improve this answer
























        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.






        share|improve this answer






















          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.






          share|improve this answer












          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.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered 3 hours ago









          Bathsheba

          169k26239355




          169k26239355




















              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.






              share|improve this answer


























                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.






                share|improve this answer
























                  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.






                  share|improve this answer














                  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.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 1 hour ago

























                  answered 1 hour ago









                  Arne Vogel

                  3,29711023




                  3,29711023



























                       

                      draft saved


                      draft discarded















































                       


                      draft saved


                      draft discarded














                      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













































































                      Comments

                      Popular posts from this blog

                      Long meetings (6-7 hours a day): Being “babysat” by supervisor

                      Is the Concept of Multiple Fantasy Races Scientifically Flawed? [closed]

                      Confectionery