C + + function template and class template -- Generic Programming

Posted by daglasen on Sat, 01 Jan 2022 17:06:02 +0100

The so-called function template is actually to establish a general function. Its function type and formal parameter type are not specified, but are represented by a virtual type. This generic function is called a function template.
All functions with the same function body can be replaced by this template. There is no need to define multiple functions, just define them once in the template.

A function template
1) Why is there a function template?
The function business logic is the same, but the function parameter types are different. Generic programming is introduced to facilitate programmer programming.
2) Syntax:
template <typename T>
void myswap(T &a,T &b)
{
}
a: tempalte tells C + + to do extensive programming and don'T report errors when you see T.
B: t indicates type
3) Call
a: Explicit call:
myswap(x,y);
myswap(a,b);
b: Automatic class derivation
myswap(x,y);// Automatic matching according to parameter type

#include <iostream>
using namespace std;

// The business logic of the function is the same 
// The parameter types of the function are different
void myswap01(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}

void myswap02(char &a, char &b)
{
char c = 0;
c = a;
a = b;
b = c;
}

template <typename T>
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ....I'm a template function welcome calll I" << endl;
}
void main()
{
    {
    int x = 10; 
    int y = 20;

    myswap<int>(x, y); //1 function template display type call

    myswap(x, y);  //2 automatic type derivation
    printf("x:%d y:%d \n", x, y);
}
{
    char a = 'a'; 
    char b = 'b';
    myswap<char>(a, b); //1 function template display type call
    myswap(a, b);
    printf("a:%c b:%c \n", a, b);
}
}


4 function template as function parameter

#include <iostream>
using namespace std;

//Let int array sort

template <typename T,typename T2 >
int mySort(T *array, T2 size)
{
T2 i, j ;
T tmp;
if (array == NULL)
{
    return -1;
}

//choice  
for (i=0; i<size; i++)
{
    for (j=i+1; j<size; j++)
    {
        if (array[i] < array[j])
        {
            tmp = array[i];
            array[i] = array[j];
            array[j] = tmp;
        }
    }
}
return 0;
}

template <typename T, typename T2>
int myPrint(T *array, T2 size)
{
T2 i = 0;
for (i=0; i<size; i++)
{
    cout << array[i] << " ";
}
return 0;
}

void main()
{
//char type
{
    char buf[] = "aff32ff2232fffffdssss";
    int len = strlen(buf);

    mySort<char, int>(buf, len);
    myPrint<char , int>(buf, len);

}

cout<<"hello..."<<endl;
system("pause");
return ;
}


5 function template encounters function overload
1) Difference between function template and ordinary function:
Function templates do not allow automatic type conversion. Ordinary functions can perform automatic type conversion

int a = 10;
char c = 'z';
myswap(a, c); // Call of ordinary functions: implicit type conversion can be performed 
myswap(c, a); //This is the time to call only ordinary functions


2) Call rules when a function template encounters an ordinary function:
a: Function templates can be overloaded like normal functions
b: When the function template and ordinary functions are consistent with the call, the C + + compiler gives priority to ordinary functions
c: If the function template can produce a better match, select the template
d: You can restrict the compiler to match only through the template through the syntax of the empty template argument list, for example: myswap < > (a, b)

#include "iostream"
using namespace std;

int Max(int a, int b)
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}

template<typename T>
T Max(T a, T b)
{
    cout<<"T Max(T a, T b)"<<endl;
    return a > b ? a : b;
}

template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}

void main()
{
int a = 1;
int b = 2;

cout<<Max(a, b)<<endl; //When both the function template and the ordinary function are consistent with the call, the ordinary function is preferred
cout<<Max<>(a, b)<<endl; //If the function template is displayed, the < > type list is used

cout<<Max(3.0, 4.0)<<endl; //If the function template produces a better match, use the function template

cout<<Max(5.0, 6.0, 7.0)<<endl; //heavy load

cout<<Max('a', 100)<<endl;  //Implicit type conversion can be achieved by calling ordinary functions 
system("pause");
return ;
}


Analysis of compiler template mechanism
The compiler does not treat the function template as a function that can handle any class
2. The compiler generates different functions from function templates through specific types
3. The compiler will compile the function template twice: compile the template code itself where it is declared; Compile the code after parameter replacement where called.

Preliminary knowledge of four types of templates
1 Why do I need a class template
1) Sometimes, there are two or more classes whose functions are the same, but the data types are different
2) The class template is used to parameterize the type of data required by the class
3) Class templates are particularly important in representing data structures such as arrays, tables, graphs, etc.)
2 single class template syntax
template <\typename type>
class Tclass{
//At least one member variable is of type type
};

template<\typename T>  
class A   //A is a template class (or class template)
{ 
public: 
     A(T t) 
     { 
          this->t = t; 
     } 
     T &getT() 
     { 
               return t; 
     } 
private: 
     T t; };

void main() 
{ 
//If a constructor is used in the template, the calling rules of the constructor of the previous class are followed 
     A<int> a(100); //Type materialization is required, otherwise an error is reported.
     a.getT(); 
     printAA(a); 
     return ;
}


3 derive common class from template class
When a subclass inherits from a template class, the compiler needs to know what the data type of the parent class is
(essence of data type: alias of fixed size memory block) a < \ int >

 

4 derive template class from template class
template <\typename T>
class C :public A<\T>
{
};

#include <iostream>
using namespace std;

//template class 
template <class T>
class A
{
public:
A(T a)
{
    this->a = a;
}
public:
void printA()
{
    cout << "a: " << a << endl;
}
protected:
T a;
};

//Ordinary class derived from template class
// When deriving from a template class, you need to materialize the template class The C + + compiler needs to know what the data type of the parent class looks like
    //=====>You need to know the memory size of the parent class. Only when the data type is fixed can you know how to allocate memory 
class B : public A<int>
{
public:
B(int a=10, int b=20) : A<int>(a)//If the parent class calls the parameterized constructor and needs to display initialization, use the object initialization list
{
    this->b = b;
}
void printB()
{
    cout << "a:" << a << " b: " << b << endl;
}
private:
int b;
};
//Derive template class from template class

template <typename T>
class C : public A<T>
{
public:
C(T c, T a) : A<T>(a)
{
    this->c = c;
}
void printC()
{
    cout << "c:" << c <<endl;
}
protected:
T c;
};

void main()
{
B  b1(1, 2);
b1.printB();

C<int> c1(1, 2);
c1.printC();

system("pause");
}

//Class template as function parameter

//Parameter, the C + + compiler requires specific classes, so a < int > & A 
void UseA( A<int> &a )
{
a.printA();
}

void main61()
{
//Template class (itself typed) = = = = = specific class = = = = > define specific variables

A<int> a1(11), a2(20), a3(30); //Template class is abstract = = = = > type specific

UseA(a1);
UseA(a2);
UseA(a3);

cout<<"hello..."<<endl;
system("pause");
return ;
}


5 knowledge system
1) All class template functions are written inside the class

#include <iostream>
using namespace std;

template <typename T>
class Complex
{
friend Complex MySub(Complex &c1, Complex &c2)
{
    Complex tmp(c1.a - c2.a, c1.b - c2.b);
    return tmp;
}

friend ostream & operator<<(ostream &out, Complex &c3)
{
    out <<  c3.a << " + " << c3.b <<  "i" << endl;
    return out;
}
public:
Complex(T a, T b)//There is no need to write template < typename T > inside the class
{
    this->a = a;
    this->b = b;
}

Complex operator+ (Complex &c2)
{
    Complex tmp(a+c2.a, b+c2.b);
    return tmp;
}

void printCom()
{
    cout << "a:" << a << " b: " << b << endl;
}
private:
T   a;
T   b;
};
//Normal writing of operator overloading 
// Overloads < > > can only use friend functions. Other operator overloads should be written as member functions. Do not abuse friend functions
void main()
{
//You need to materialize the template class before you can define the object. The C + + compiler needs to allocate memory
Complex<int>    c1(1, 2);
Complex<int>    c2(3, 4);

Complex<int> c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;

//Abuse of friend functions
{
    Complex<int> c4 = MySub(c1, c2);
    cout << c4 << endl;

}

cout<<"hello..."<<endl;
system("pause");
return ;
}


2) All class template functions are written outside the class in a cpp

#include <iostream>
using namespace std;

 //Friend function: friend function is not an implementation function overload (not <, > >) 
 //The pre declaration of the class and the pre declaration of the function need to be added before the class 
template <typename T>
class Complex ; //Class

template <typename T>
Complex<T> MySub (Complex<T> &c1, Complex<T> &c2);
template <typename T>
class Complex
{
friend Complex<T> MySub<T> (Complex<T> &c1, Complex<T> &c2);

friend ostream & operator<< <T> (ostream &out, Complex &c3);//Solution: add < T > here

public:
Complex(T a, T b);
void printCom();
Complex operator+ (Complex &c2);    

private:
T   a;
T   b;
};

//The implementation of the constructor is written outside the class
template <typename T>   //This sentence should be added before each template function
Complex<T>::Complex(T a, T b)//Class templates need to be materialized
{
    this->a = a;
this->b = b;
}

template <typename T>
void Complex<T>::printCom()
{
cout << "a:" << a << " b: " << b << endl;
}

//The essence is: the template is generated by two compilations. The function header generated for the first time is different from that generated for the second time
    //Member function implementation + operator overloading
template <typename T>
Complex<T>  Complex<T>::operator+ (Complex<T> &c2)//Class templates need to be materialized
{
Complex tmp(a+c2.a, b+c2.b);//You can add < T > here or not
return tmp;
}

//Friend function implementation < < operator overloading
template <typename T>
ostream & operator<<(ostream &out, Complex<T> &c3)
{
out <<  c3.a << " + " << c3.b <<  "i" << endl;
return out;
}
//Abuse of friend functions. Conclusion: don't use friend functions where you don't use friend functions
template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2)
{
Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}

void main()
{
//You need to materialize the template class before you can define the object. The C + + compiler needs to allocate memory
Complex<int>    c1(1, 2);
Complex<int>    c2(3, 4);

Complex<int> c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;

//Abuse of friend functions
{
    Complex<int> c4 = MySub<int>(c1, c2);
    cout << c4 << endl;
}
cout<<"hello..."<<endl;
system("pause");
return ;
}


Keyword static in six types of templates

/*

The compiler does not treat function templates as functions that can handle any class
The compiler generates different functions by specific types from function templates
The compiler compiles the function template twice
Compile the template code itself where it is declared; Compile the code after parameter replacement where called.
*/

#include <iostream>
using namespace std;

template <typename T>
class AA
{
public:
static T m_a;
};

template <typename T>
T AA<T>::m_a  = 0;
class AA1
{
public:
   static int m_a;


};
int AA1::m_a = 0;

class AA2
{
public:
static char m_a;
};
char AA2::m_a  = 0;
void main()
{
AA<int> a1, a2, a3;
a1.m_a = 10;
a2.m_a ++;
a3.m_a ++;
cout << AA<int>::m_a << endl;

AA<char> b1, b2, b3;
b1.m_a = 'a';
b2.m_a ++;
b2.m_a ++ ;

cout << AA<char>::m_a << endl;

//m_a should be that each type of class uses its own m_a

cout<<"hello..."<<endl;
system("pause");
return ;
}


--------
Copyright notice: This is the original article of CSDN blogger "little poplar on the IT road", which follows the CC 4.0 BY-SA copyright agreement. For reprint, please attach the original source link and this notice.
Original link: https://blog.csdn.net/weixin_40878579/article/details/81369398

Topics: C++