Templates have been helping programmers to change the way they are able to code since the ealy 1990s. Since that time they have advanced rapidly, and in this tutorial we will take a close look at them and learn to use them to effectively decrease our tasks.
Templates in C++ - Starting with Templates (Page 2 of 4 )
This article requires a solid grasp of C++ and some additional OOP (Object Oriented Programming) knowledge is also recommended. Templates are at the end of most C++ manuals because I think most high schools skipped over this issue; maybe the teachers didn't want to stress the students. Who knows?
But templates can be easily understood if you get the basic concepts. So I'll start here and present the way templates work. This way you can better understand all the template libraries present "on the market."
Defining templates is quite easy once you know how to do it. A template is "something that establishes or serves as a pattern," according to the Webster dictionary. In C++, this is also its main purpose.
Think of a sorting algorithm, for example. We can agree that the algorithm in its base form is the same regardless of whether we need to order integers, doubles, or an array of strings. The only difference is in the method we use to compare the two items. In C, the problem of eliminating the redundant code has been partially solved by passing the type of the item in the function so we can step through the items with the size. A classic example is quicksort(qsort), which is written in stdlib.h as follows:
Furthermore, this method is quite complex. Later on, we would soon face problems with advanced data structures. The solution to anything written for generalized types is templates. Templates, once created, are generated by the compiler for each individual type used in the program. As a warm-up, take a look at the code below; it's a function template:
template< typename Type,class T>
int function( Type first, T second)
return memcmp(first, second,sizeof(first));
The function compares the two types by memory after the size of the first and returns the result. If you have a compiler, you may copy the above line and compile it. However, with templates, if the compilation process gives the blessed 0 error(s), 0 warning(s) message, it doesn't mean you got it right. You should always remember that templates aren't constructed until they are initialized.
You can try the following: put fiarst in the sizeof instead of first. If you recompile it, you'll observe that there are still no errors; it's obvious that this situation should pop up an undeclared fiarst variable.
This is because the compiler doesn't create the function until it must use it or, as we should say, until you call it. Call the function and you should have your error message. Also, you'll notice something that will haunt you whenever you use templates and get something wrong: the rude error messages. You can easily generate 300+ line errors and 100+ error messages with just a missing (or extra) comma, for example. This is not good.
So empower yourself with patience, because you're going to need it to understand the error messages reported by the compiler. In some cases, the error will be in a different place than reported (usually in the STL base because it will manifest the effect of the error in your code there). The bottom line is that you should have a good comprehension of what you're doing and where the source of the error could be.
And the usage of the function is nothing special:
result = function(a, b);
cout << result << endl;
result = function(b, a);
cout << result << endl;
Let's see what can we deduce by switching back to the original function from the upper block of code. To create a template, first you need to add it before the function and enter it within the types. Introducing types can be completed by the class or the typename keyword. Each of them means the same thing and can be used interchangably.
For example, in the upper function, Type and T will be replaced upon the function's creation with the type they are called. The compiler is smart enough to deduce the types and replace them with the correct ones. This is done during compilation. This means that it will take a little longer to compile, so expect this when deciding whether or not to use templates.
Returning to our situation, in the function, all the Type words will be replaced with the type that is called, so you can use them exactly the same way you are accustomed to using any kind of type names: declare local variables, pointers to that variable, etc.
However, if you are more of a rebel you can directly specify what types to call/create. For the upper code, the same effect is created if we call it by specifying the type like this:
result = function<int*,char*>(a, b);
Nevertheless, this can be unnecessary code because the compiler checks what types are called and generates the corresponding functions. Thus, in our case, there are going to be two functions generated: an int*, char* parameter for the first call and a char*, int* for the second call. Due to this, the binary file generated by the compiler will be a little larger, but we managed to get away with just one function written by us.
If you need more template variables, you declare more of them in the <> brackets of the template keyword. Any type can be called to generate the function; it will be generated a template for anything: structure, int, long, float, classes, etc. However, you should be aware that the memcmp requires addresses, so you'll have to send the item's pointers, but I think you get the big picture.