It seems you misunderstood what polymorphism is.
Polymorphism, in its essence, has nothing to do with derived classes. Polymorphism simply means the ability to use a type without knowing it. Instead of using a specific type, polymorphism relies on a prototype of some form to determine what types it takes. Any types that match this prototype are accepted.
Runtime politicism in C ++ is provided by retrieving classes from a base class that contains virtual functions. The base class and virtual functions form a polymorphic prototype. Code written to accept the base class that calls these virtual functions will accept any instance of the class derived from the base class.
A compile-time polymorphism is a polymorphism that occurs ... at compile time;) This means that the compiler needs to know what is happening. You may have written C ++ code with a polymorphic prototype, but the compiler doesn't care. You get specific concrete types after compilation.
Polymorphism of compilation time is provided by templates in C ++. A function or class of a template can take any type that corresponds to a prototype, commonly called a "concept." Unlike base classes and virtual functions, the prototype is implicit: the prototype is determined only by how the type is used by the function / class of the template.
If you have this template function:
template<typename T> void Stuff(T &t) { t.call(15); }
There is an implicit requirement in T This requirement is that it has a member function called call . There must be one overload of this member function that can be called with an integer value.
This means that any type that is suitable for this prototype can be used.
Pattern polymorphism is broader than inheritance polymorphism, as it can be used by a wider array of types. A type must be specifically designed to use inheritance polymorphism; you have to learn from the class. A type can be non-destructive (i.e. you do not need to change the type itself), adapted to template polymorphism. Even if your prototype template is well designed:
template<typename T> void Stuff(T &t) { call(t, 15); }
All that is required for this version of Stuff is that there is some function that takes a T& value and an integer value. If I have some type that I want to use with Stuff , all I have to do is define the call function in the corresponding namespace (namely, in the namespace in which the type was specified). And that will work just fine. All without changing the type itself.
Of course, compilation time polymorphism is ... compilation time. If I want some user input or data file to select a polymorphic type, templates are not going to help much (although type erasing, a template-based method, can help). The principal advantage of runtime polymorphism is that it is truly runtime.
Another advantage is that he is more accurate about his prototypes. Everything is explicitly said about inheritance. Obviously, a virtual functional interface in the base class. The compiler will not allow you to misuse this base class (calling methods that do not exist on it). Indeed, a decent IDE will define your code so that you only see methods in the base class.
Pattern polymorphism is much more implicit. Since C ++ does not have the ability to indicate the prototype that a particular function / class of the template is placed in the type, it is very easy to accidentally call something by the type of the template that you should not. The compiler will only detect this when trying to use a type that does not match the prototype. And even then you will usually get a massive error (depending on how deeply your code template is nested), which makes it difficult to identify the problem.
It is also much more difficult to implement an implicit template polymorphic prototype, since it is not registered. Implementation of a derived class requires passing through the base class, viewing all virtual functions and their implementation. Doing this for a prototype template is much more difficult, unless there is documentation that says so. If you are unable to implement something, you will again receive an error, which is usually less than the upcoming problem.