dynamic_cast can only be used with pointers and references to classes
(or with void*). Its purpose is to ensure that the result of the type
conversion points to a valid complete object of the destination
This naturally includes pointer upcast (converting from
pointer-to-derived to pointer-to-base), in the same way as allowed as
an implicit conversion.
But dynamic_cast can also downcast (convert from pointer-to-base to
pointer-to-derived) polymorphic classes (those with virtual members)
if -and only if- the pointed object is a valid complete object of the
Converting a pointer-to-derived-type into a pointer-to-base-type can be done without knowing anything about the exact type of the object that's being pointed to, because the derived type is always an instance of the base type. That is, the conversion depends only on static information. So
dynamic_cast in that direction is always okay, because it can always be done.
dynamic_cast to convert the other way requires knowing the actual type of the object being pointed to, because without that information there's no way to know whether the conversion is valid. For example:
Base* bp = get_base(); Derived* dp = dynamic_cast<Derived*>(bp);
In this situation, if
bp in fact points to an object of type
Base the conversion is not valid; if it points to an object of type
Derived or a type derived from
Derived, the cast is okay.
To determine the exact type of the object at runtime, the support code relies on embedded type information that is only required to be present for polymorphic types. That's why the base-to-derived cast requires a polymorphic type: it ensures that the required information is present.
Granted, the standard could have made the derived-to-base conversion invalid for non-polymorphic types, but that's a procrustean restriction; there's no good reason for prohibiting it.