Generally speaking, in 90% of situation templates and virtual functions are interchangeable.
First of all, we need a clarification what we are talking about. If you "compare" something, it must be in some criteria equivalent. My understand of your statement is not comparing virtual functions
with templates
but within the context of polymorphism
!
Your examples are not well selected in that case and also dynamic cast
is more a "big hammer" out of the toolbox as if we talk about polymorphism.
Your "template example" did not need to use templates as you have simple overloads which can be used without any templated code at all!
If we are talking of polymorphism
and c++
we have at a first selection runtime-polymorphism
and compile-time
polymorphism. And for both we have standard solutions in c++. For runtime we go with virtual functions, for compile-time polymorphism we have CRTP as a typical implementation and not templates as a general term!
are there any comments or recommendations from C++ committee or any other authoritative source, when we have to prefer ugly syntax of templates over much more understandable and compact virtual functions and inheritance syntax?
The syntax isn't ugly if you are used to use it! If we are talking about implementing things with SFINAE, we have some hard to understand rules with template instantiation, especially often misunderstood deduced context
.
But in C++20 we will have concepts, which can replace SFINAE in most contexts which is a great thing I believe. Writing code with concepts instead of SFINAE makes it more readable, easier to maintain and a lot easier to extend for new types and "rules".
Standard library if full of templates and has a very limited amount of virtual functions. Does it mean that we have to avoid virtual functions as much as possible and prefer templates always even if theirs syntax for some specific task is much less compact and much less understandable?
The question feels you misunderstood the C++ features. Templates allows use to write generic code
while virtual functions
are the C++ tool to implement runtime polymorphism
. There is nothing which is 1:1 comparable.
While reading your example code, I would advice you to think again about your coding style!
If you want to write functions specific for different data types, simply use overloads as you did in your "template" example, but without the unneeded templates!
if you want to implement generic functions which works for different data types in the same code, use templates and if some exceptional code is needed for specific data types use template specialization for the selected specific code parts.
If you need more selective template code which needs SFINAE you should start implementing with c++20 concepts.
If you want to implement polymorphism
decide to use run time
or compile time
polymorphism. As already said, for the first one virtual functions are the standard C++ tool to implement that and CRTP is one of standard solutions for the second one.
And my personal experience with "dynamic cast" is: Avoid it! Often it is a first hint that something is broken with your design. That is not a general rule but a check point to think again about the design. In rare cases it is the tool which fits. And in RTTI is not available for all targets and it has some overhead. On bare metal devices/embedded systems you sometimes can't use RTTI and also exceptions. If your code is intended to be used as a "platform" in your domain, and you have the mentioned restrictions, don't use RTTI!
EDIT: Answers from the comments
So, for now, with C++ we can make classes hierarchy with run-time polymorphism only.
No! CRTP builds also class hierarchies but for compile time polymorphism. But the solutions is quite different as you don't have a "common" base class. But as all is resolved in compile time, there is no technical need for the common base class. You simply should start reading about Mixins, maybe here: What are Mixins (as a concept) and CRTP as one of the implementation methods: CRTP article wikipedia.
don't know how to implemented something similar to virtual functions without run-time overhead.
See above CRTP and Mixin exactly implementing polymorphism without runtime overhead!
Templates give some possibility to do that.
Templates are only the base C++ tool. Templates are the same level as loops in C++. It is much to broad to say "templates" in this context.
So, if we need class hierarchy, does it mean that we have to use it even it will force us to use less compile time checks?
As said, a class hierarchy is only a part of the solution for the task to implement polymorphism. Think more in logical things to implement like polymorphism, serializer, database or whatever and the implementation solutions like virtual functions, loops, stacks, classes etc. "Compile time checks"? In most cases you don't have to write the "checks" your self. A simple overload is something like an if/else in compile time which "checks" for the data type. So simply use it out of the box, no template nor SFINAE is needed.
Or we have to use templates to implement some sort of compile-time classes hierarchy even it will make our syntax much less compact and understandable
Already mentioned: Template code can be readable! std::enable_if
is much easier to read as some hand crafted SFINAE stuff, even both uses the same C++ template mechanics. And if you get familiar with c++ 20 concepts, you will see that there is a good chance to write more readable template code in the upcoming c++ version.