[C++ Breaking] Generic Programming | Function Templates | Class Templates

?Author homepage

lovewold less r blog homepage

Key points of this article: Explanation of basic knowledge points of c + + templates

[C-C++ Getting Started Series Column]: Blog article column portal

Word of the Day: Flowers bloom again, and people are no longer young

Directory

Preface

Generic programming

function template

Function template concept

Function template format

The principle of function template

Instantiation of function template

Matching principles for template parameters

class template

Class template definition format

Instantiation of class template

Summarize

Foreword

C++ is an object-oriented language. In many cases, we don’t need to think too much about the bottom layer when writing programs. The input and output of C++ we learned earlier seemed to be like this. The compiler will automatically do a lot of things for us without having to pass the input and output variable types and other factors ourselves. This method is definitely easy for people who write programs. The progress of various technologies in the world is actually inseparable from people’s pursuit of laziness and obsession with convenience, and the template I am going to explain today seems to be a special product.

First, let’s write a relatively simple program to explore in detail why we need templates. Here we wrote a function for exchanging integer variables. The exchange function is a commonly used function no matter what kind of sorting or some calculations.

void swap(int & amp; x, int & amp; y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10;
int b = 5;
cout << "a=" << a << " " << "b=" << b << endl;
swap(a, b);
cout << "swap~" << endl;
cout << "a=" << a << " " << "b=" << b << endl;
return 0;
}

Here comes the problem. We need to emphasize here that this is an exchange function for integer types, so it is not suitable for other types of variables. So how do we implement long integer, short integer, floating point type, character type… Function overloading?

C++ provides a method of function overloading. For some parameters, the corresponding function will be directly determined by the parameters passed.

But the flaws are also obvious. Our functions are only of different types. Do we need to overload all types? Or should we create the types we need first, and when new types appear, users can then add corresponding function overloads themselves.

Another key point is that we are just exchanging functions here, which is relatively simple. However, for a code with various functions nested in each other, if one code goes wrong, all other overloaded functions must be changed, and the maintainability of the code is relatively low.

Generic Programming

If we look at the previous code again, we will find that it is just a difference in type. Can we provide a way to leave the task of type identification to the compiler like cout and cin? We only need to pass parameter variables to it. Can. In other words, we only need to provide a code as a mold, and the compiler can use this mold to generate corresponding code according to different types.

void swap(int & amp; x, int & amp; y)
{
int temp = x;
x = y;
y = temp;
}
void swap(double & amp; x, double & amp; y)
{
double temp = x;
x = y;
y = temp;
}
void swap(char & amp; x, char & amp; y)
{
char temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10;
int b = 5;
cout << "a=" << a << " " << "b=" << b << endl;
swap(a, b);
cout << "swap~" << endl;
cout << "a=" << a << " " << "b=" << b << endl;
return 0;
}

Whether it is movable type printing or the current mold casting technology, the fundamental purpose is to maintain the same function. You can inject different materials to change the effect of the final product, but essentially the function is the same, and the appearance is Consistent.

The reason why CV engineers can have unique CV methods is because predecessors have specific boards that are already usable, and they only need to modify them and then adapt them to their own project content to achieve a brand new project result. This is just a conventional method of code reuse.

Without further ado, let’s get straight to the point. Let’s first talk about what generic programming is.

Generic programming is a programming paradigm whose goal is to write generic code that is not tied to a specific data type, allowing for wider reuse of the code. Generic programming allows programmers to write data type-independent algorithms and data structures, thereby increasing code flexibility, reusability, and maintainability.

Specifically, generic programming is implemented through the use of parameterized types(parameterized types). A parameterized type is an abstract type that allows the use of unspecified concrete types in code. This way, algorithms and data structures can be written without having to specify specific data types at the time of writing. Where these algorithms and data structures need to be used, parameters can be specified by providing specific types.

In C++, generic programming is primarily implemented through templates. Templates allow programmers to write data type-independent code that can be used for different data types. This makes it possible to reuse code in different contexts. For example, you can write general sorting algorithms, container classes, and other algorithms and data structures without having to write a specific set of code for each data type. Template is the basis of generic programming.

Function Template

Function template concept

Function templates are a mechanism in C++ for creating generic functions, allowing programmers to write function code that is independent of a specific data type. Function templates are implemented through the use of parameterized types, making it possible to use abstract types without specifying concrete types when writing code. In this way, function templates can be applied to multiple data types, improving the flexibility and reusability of the code.

Function template format

template<typename T>
void Swap(T & amp; x,T & amp; y)
{
    T temp = x;
    x = y;
    y = temp;
}

template T represents template parameters, T is a placeholder, representing any data type. In a function template, you can use T as the parameter type and return type of the function, and to perform common operations in the function body.

Note: typename is used to define template keywords, and class can also be used (struct cannot be used instead of class)

Principle of function template

From the agricultural period to the industrial period, many repetitive mechanical tasks were directly handed over to machines, which greatly liberated productivity. Machine production eliminates many hand-created things, which essentially leaves these tasks to machines.

The function template itself is not a function, but serves as a blueprint like a class. It is a mold for the compiler to generate specific types of functions in a usage manner.

In the compiler compilation phase, for the use of template functions, the compiler needs to deduce and generate corresponding types of functions for calling based on the types of actual parameters passed in. For example, when the int type uses a function template, the compiler determines T as an int type based on the deduction of the passed actual parameter type, and then specifically generates a code to process the int type. This is true for both the double type and the character type.

Instantiation of function template

When using a function template with different types of parameters, it is called an instantiation of the function template. Template parameter instantiation is divided into implicit instantiation and explicit instantiation.

Implicit instantiation: Let the compiler deduce the actual type of the template parameter based on the passed argument type.

template <class T>
T Add(const T & amp; left, const T & amp; right)
{
return left + right;
}

int main()
{
int a1 = 10;
int a2 = 5;
Add(a1, a2);

double b1 = 10.0;
double b2 = 5.0;
Add(b1, b2);
\t
return 0;
}

When calling, mixing parameters of different types is not allowed, because the compiler needs to deduce during compilation. A1 can be deduced as int, and b1 can be deduced as double, but there is only one T in the template parameter list. The compiler It is impossible to determine whether T is replaced by int or double and an error is reported. In templates, the compiler generally does not perform type conversion, otherwise the compiler will bear considerable consequences if an error occurs.

Therefore, we usually have two ways to solve this problem: users perform forced type conversion themselves or use display instantiation.

Explicit instantiation: Specify the actual type of the template parameter after the function name

int main()
{
int a1 = 10;
int a2 = 5;
Add(a1, a2);

double b1 = 10.0;
double b2 = 5.0;
Add(b1, b2);
Add<int>(a1, b1);//Display instantiation
return 0;
}

Matching Principles of Template Parameters

A non-template function and a template function can exist at the same time, and the template function can also be instantiated as the non-template function. (This kind of instantiation as a non-template function means that the parameter passing may remain the same, but according to the principle of priority matching, some special processing can be done when calling the function). Here we check the calling priority by printing information between different functions.

//Specially handles integer type addition functions
int Add(int left, int right)
{
cout << "Non-template function" << endl;
return left + right;
}
//General addition template
template<class T>
T Add(T left, T right)
{
cout << "template function" << endl;
return left + right;
}
void test()
{
Add(1,2);//Match non-template functions first
Add(1.0, 2.0);//Template function
Add<int>(1, 2);//Call the specific version of Add and use the template function
}
int main()
{
test();
return 0;
}

For non-template functions and function templates with the same name, if other conditions are the same, the non-function template will be called first instead of generating an instantiated function from the function template. If a function can generate a better matching function, then select a template.

int Add(int left, int right)
{
cout << "Non-template function" << endl;
return left + right;
}
//General addition template
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
cout << "template function" << endl;
return left + right;
}
void test()
{
Add(1, 2);//Match non-template functions first
Add(1, 2.0);//With a more matching version without type conversion, the compiler gives priority to generating a more matching version of the Add function
}
int main()
{
test();
return 0;
}

Template functions cannot perform automatic type conversion (it is busy enough to help you deduce it. If you have to scold him if there is an error in type conversion, he will naturally not help you with this meaningless thing). But ordinary functions can perform automatic type conversion

Class Template

Definition format of class template

template<class T1, class T2, class T3>//The parameter list can define multiple template variables
class class template name
{
//Class member definition
};

As we mentioned in the learning sequence table earlier, use typedef to rename the type name. Type renaming in C language refers to creating a new alias for an existing type by using the typedef keyword. This can simplify the code, improve readability, and facilitate batch modification of specific types, making it easier to maintain the code. But when we learned about class templates, we found that we don’t need to do this, but use class templates. Here we briefly construct a class template of a dynamic sequence table to experience it.

template<class T>
class Vector
{
public:
Vector(size_t capacity = 10)
: _pData(new T[capacity])
,_size(0)
, _capacity(capacity)
{}

~Vector();

// Other member functions go here...

size_t Size() { return _size; };

T & operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}

private:
T*_pData;
size_t _size;
size_t _capacity;
};

// Class template function definitions can be placed outside the class definition.
template<class T>
Vector<T>::~Vector()
{
if(_pData)
{
delete[] _pData;
}
_size = _capacity = 0;
}

instantiation of class template

The instantiation of a class template is different from the instantiation of a function template. The instantiation of a class template needs to be preceded by <> before the name of the class template, and then the type of instantiation needs to be placed in <> (just like it must be explicitly instantiated ). The class template name is not the real class, but the instantiation result is the real class (the relationship between blueprints and building with blueprints). When instantiated, the compiler generates a class definition for the specific data type, making the class template concrete and can be used like an ordinary class.

Vector class name, Vector is the type
Vector s1;
Vector s2;

Summary

Generic programming is a programming paradigm whose goal is to write reusable, general-purpose code that can accommodate multiple data types without having to repeatedly write similar code for each type. In C++, generic programming is mainly implemented through function templates and class templates. Its advantages are as follows:

  • Improve code reusability and maintainability.

  • Allows abstraction on different data types and reduces code redundancy.

Function Templates:

Concept: Function template is a way to define a general function, in which the parameters or return types of the function can be general type parameters.

Syntax:

template <class T> //typename can also be used
T Add(T a, T b)
{
    return a + b;
}

Instantiation: By specifying a specific data type, the compiler will generate a function definition of the corresponding type.

Instantiation usage:

int r_int = Add(3, 4); // Instantiated as Add<int>(3, 4)
double r_double = Add(3.14, 2.5); // Instantiated as Add<double>(3.14, 2.5)

Class Templates:

Concept: Class templates are a way of defining a generic class, where members or behavior can depend on generic type parameters.

Syntax:

 template <class T>
   classMyClass
   {
   public:
       MyClass(T value) : data(value) {}
       void play() { /* ... */ }
   private:
       T data;
   };

Instantiation: By specifying a specific data type, the compiler will generate a class definition for the corresponding type.

Use:

 MyClass<int> intObject(42);
   MyClass<double> doubleObject(3.14);

The author’s level is limited, please correct me if there are any mistakes!

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Algorithm skill tree Home page Overview 57574 people are learning the system

syntaxbug.com © 2021 All Rights Reserved.