C ++ Template Basics: Function Templates

  • Tutorial
Disclaimer: the article was started back in February, but, for reasons that depend on me, it was not finished. The topic is very extensive, so it is published in a truncated form. What did not fit will be considered later.



It is impossible to understand modern C ++ without knowing what programming patterns are. This property of the language opens up wide possibilities for optimizing and reusing code. In this article, we will try to figure out what it is and how it all works.

The template mechanism in C ++ allows us to solve the problem of unifying the algorithm for various types: there is no need to write different functions for integer, real or user types - it is enough to compile a generalized algorithm that is independent of the data type, based only on common properties. For example, the sorting algorithm can work with integers as well as with objects of the “car” type.

There are function templates and class templates.

Function Templates-– This is a generalized description of the behavior of functions that can be called for objects of different types. In other words, a function template (template function, generalized function) is a family of different functions (or an algorithm description). By description, the function template is similar to a regular function: the difference is that some elements are not defined (types, constants) and are parameterized.

Class templates - a generalized description of a user type in which attributes and type operations can be parameterized. They are constructions by which real classes can be generated by substituting concrete arguments instead of parameters.

Let's consider function templates in more detail.

Function Templates


How to write the first template function?


Consider the case of determining the minimum element of two. In the case of integers and real numbers, you will have to write 2 functions.

int _min(int a, int b){
    if( a < b){
        return a;
    }
    return b;
}
double _min(double a, double b){
    if( a < b){
        return a;
    }
    return b;
}

You can, of course, implement only one function, with valid parameters, but for understanding the patterns this will be harmful.
What happens if the application is compiled? Both implementations of the function will fall into the binary code of the application, even if they are not used (however, now the compilers are very smart, they can cut out unused code). And if you need to add a function that defines the minimum of 2 lines (it is difficult to imagine without specifying that there is a minimum line) ?!

In this case, if the algorithm is common for the types that you have to work with, you can define a function template. The principle, in the general case, will be as follows:

  1. a function implementation is taken for some type;
  2. attributed title template (or template), which means that the algorithm uses some kind of abstract type Type;
  3. in the implementation of the function, the type name is replaced by Type.

For the min function, we get the following:

template
Type _min(Type a, Type b){
    if( a < b){
        return a;
    }
    return b;
}

The most interesting is the fact that while there is no call to the min function, when compiled, it is not created in binary code (not instantiated ). And if you declare a group of function calls with variables of various types, for each compiler will create its own implementation based on the template.

Calling a template function is generally equivalent to calling an ordinary function. In this case, the compiler will determine which type to use instead of Type, based on the type of the actual parameters. But if the substituted parameters turn out to be of different types, then the compiler will not be able to output (instantiate the template) implementation of the template. So, in the following code, the compiler will stumble on the third call, because it cannot determine what Type is (think about why?):

#include 
template
Type _min(Type a, Type b) {
    if (a < b) {
        return a;
    }
    return b;
}
int main(int argc, char** argv) {
    std::cout << _min(1, 2) << std::endl;
    std::cout << _min(3.1, 1.2) << std::endl;
    std::cout << _min(5, 2.1) << std::endl; // oops!
    return 0;
}

This problem is solved by specifying a specific type when calling the function.

#include 
template
Type _min(Type a, Type b) {
    if (a < b) {
        return a;
    }
    return b;
}
int main(int argc, char** argv) {
    std::cout << _min(5, 2.1) << std::endl;
    return 0;
}

When will the template function (not) work?


In principle, you can understand that the compiler simply substitutes the desired type in the template. But will the resulting function always be functional? Obviously not. Any algorithm can be defined regardless of the type of data, but it necessarily uses the properties of this data. In the case of the template function _min, this is a requirement to define an ordering operator (operator <).

Any function template assumes the presence of certain properties of a parameterized type, depending on the implementation (for example, a copy operator, a comparison operator, the presence of a specific method, etc.). In the expected C ++ language standard, concepts will be responsible for this .

Function template overload


Function templates can also be overloaded. Usually this overload is performed when

template
Type* _min(Type* a, Type* b){
    if(*a < *b){
        return a;
    }
    return b;
}

Special cases


In some cases, the function template is ineffective or incorrect for a particular type. In this case, you can specialize the template, that is, write an implementation for this type. For example, in the case of strings, you might want the function to compare only the number of characters. In case of specialization of the function template, the type for which the template is specified is not specified in the parameter. The following is an example of this specialization.

template<>
std::string _min(std::string a, std::string b){
    if(a.size() < b.size()){
        return a;
    }
    return b;
}

Specialization of the template for specific types is done again for reasons of economy: if this version of the function template is not used in the code, then it will not be included in the binary code.
For the future, multiple and integer parameters remain. A natural extension is the class templates, the basics of generative programming, and the design of the C ++ standard library. And a bunch of examples!

Also popular now: