Details on C++ Generic programming

  • 2020-07-21 09:11:45
  • OfStack

Generic programming has the same goals as object-oriented programming, even though techniques for reusing code and abstracting generic concepts are simpler. Whereas object-oriented programming emphasizes the data aspect of programming, generic programming emphasizes independence from specific data types.

This article introduces the other branch of C++ programming that is parallel to object-oriented programming -- generic programming. This article mainly introduces function templates, class templates and member templates

If there is infringement, please contact delete, if there is a mistake, welcome to correct, thank you

Generic programming

Templates are one of the important ideas of generic programming. STL(Standard Template Library, standard template library) is an example of using templates
A function template

Compared with function overloading (functions with the same name and different parameter lists in the scope of 1), function template only needs 1 function to realize some functions of function overloading (functions with the same parameter number and different types need to define multiple functions with different parameter lists of the same name).


template<typename T, typename Y> //  I could have written this  template<class T, class Y>  Here the  class  and  typename  The same effect 
void tfunc(T& t, Y& y) {
 cout << t << " " << y << endl;
}
int n = 2;
double d = 2.1;
tfunc(n, d);

//  Operation results: 2 2.1

Function template materialization is to process one (several) of the types to be processed separately and write one implementation in the form of template < > void fun(type & t); , the materialized function template and the normal function can exist at the same time, and the call order is the normal function > Function template materialization > Template function


// ======  test 1 : Function templates are specific to a particular data type  ======
struct Node {
 int val;
 Node* next;
};
//  A function template 
template<typename T> 
void tfunc(const T& t) {
 cout << "template: " << t << endl;
}
//  Function template materialization ( To deal with Node Type data )
template<> 
void tfunc<Node>(const Node& node) {
 cout << "template<Node>: " << node.val << endl;
}
//  Function template materialization ( To deal with int Type data )
template<> 
void tfunc<int>(const int& n) {
 cout << "template<int>: " << n << endl;
}
//  Common function 
void tfunc(const int& n) {
 cout << "tfunc(): " << n << endl;
}
double d = 2.1;
tfunc(d); //  The function template is not materialized double Type function that calls the template 
Node node{ 2, nullptr };
tfunc(node); //  Function template materialization Node Type a function that calls the materialization of a function template 
int n = 2;
tfunc(n); //  Function template materialization int Type functions, there are normal functions, call normal functions 
// ======  test 2 : The function template is partially materialized  ======
template<typename T1, typename T2>
void tfunc(T1 t1, T2 t2) {
 cout << typeid(T1).name() << " and " << typeid(T2).name() <<": " << t1 << " " << t2 << endl;
}
template<typename T1>
void tfunc(T1 t1, int i) {
 cout << typeid(T1).name() << " and " << "int: " << t1 << " " << i << endl;
}
template<typename T2>
void tfunc(long l, T2 t2) {
 cout << "long and " << typeid(T2).name() << ": " << l << " " << t2 << endl;
}
template<>
void tfunc(short l, int i) {
 cout << "long and int: " << l << " " << i << endl;
}
//  Call the above separately 4 A template function 
tfunc(char('c'), char('c'));
tfunc(char('c'), int(10));
tfunc(long(10), char('c'));
tfunc(short(10), int(10));

Function templates are instantiated to allow the compiler to generate function definitions of the specified type without writing the implementation of the function as template void fun(type) & t);


//  A function template 
template<typename T> 
void tfunc(const T& t) {
 cout << "template: " << t << endl;
}

//  Function template instantiation. Instead of writing the implementation of the function, the compiler generates template externalization functions of this type 
template void tfunc<char>(const char& c);

Class template

Class templates can specify default template parameters (function templates cannot), like the default value of function parameters, which must be assigned continuously from right to left. If an object is instantiated with a type passed, the default type will be overridden, like function parameters
To create an object, you need to pass in the template parameter list, which is appended to the class name ClassName < typename T > classN; If the class template parameter column

The table has default values, you can not pass template parameters, but 1 must be added < > Such as ClassName < > classN; When creating heap objects, all class names are followed by a template parameter list, such as ClassName < typename T > * classN = new ClassName < typename T > ; Anywhere ClassName appears except within the class, the template parameter list is generally added


template<typename T = int, typename Y = char> //  The template default parameters are specified here, and part of the specification must be specified from right to left 
class Test {
public:
 Test(T t, Y y) : t(t), y(y) {
 }
 void tfunc();
private:
 T t;
 Y y;
};
template<typename T, typename Y> //  The functions of the class template are implemented outside of the class and require a list of template parameters, but not the specified default template parameters 
void Test<T, Y>::tfunc() { //  Outside the class Test Template parameters need to be added 
 cout << t << " " << y << endl;
}
int n = 2;
double d = 2.1;
Test<int, double> test(n, d); //  The default template parameter can be defined as  Test<> test(int(2), char('a'));
test.tfunc();
//  Operation results: 2 2.1

There are mainly two ways to pass parameters after class template is inherited. One is to specify fixed types for the parent class directly when the child class inherits the parent class, and the other is to pass through the parameter list of the child class template


// ======  test 1 ======
template<typename T, typename Y>
class A {
public:
 A(T t, Y y) {
 }
};
class Test : public A<int, double> { //  The superclass is a class template, and the subclass is a normal class 
public:
 Test() : A<int, double>(2, 2.1) {
 }
};
Test();
// ======  test 2 ======
template<typename T, typename Y>
class A {
public:
 A(T t) {
 }
};
template<typename X, typename Z, typename P>
class Test : public A<X, P> {
public:
 Test(X x, Z z, P p) : A<X, P>(x) {
 }
};
Test<int, double, char>(int(2), double(2.1), char('a'));

The polymorphism of class templates, when creating objects, is divided into subclasses without templates (CFather < short, char > * cf = new CSon;) And subclasses have templates (CFather < short, char > *cf = new CSon < short, int, char > ), the subclass and the superclass template parameter list may not be the same, but the 1 must correspond well


// ======  test 1 ======
template<typename T, typename Y> 
class A {
public:
 virtual void tfunc(T t, Y y) = 0;
};
class Test : public A<int, double> { 
public:
 virtual void tfunc(int n, double d) {
 cout << n << " " << d << endl;
 }
};
//  The superclass is the class template, the subclass is the ordinary class, in the polymorphic case the superclass needs to specify the template parameter, the subclass does not 
A<int, double>* a = new Test;
a->tfunc(2, 2.1);
//  Operation results: 2 2.1
// ======  test 2 ======
template<typename T, typename Y> 
class A {
public:
 virtual void tfunc(T t, Y y) = 0;
};
template<typename X, typename Z, typename P>
class Test : public A<X, P> {
public:
 virtual void tfunc(X x, P p) {
 cout << x << " " << p << endl;
 }
};

//  The superclass is the class template, and the subclass is the class template. In the case of polymorphism, both the superclass and the subclass need to specify template parameters 
A<int, double>* a = new Test<int, char, double>;
a->tfunc(2, 2.1);
//  Operation results: 2 2.1

The reification of a template can be divided into two types: partial reification and full reification


template<typename T1, typename T2>
class Test {
public:
 Test() {
 cout << "T1 and T2" << endl;
 }
};
//  Partial reification 
template<typename T1>
class Test<T1, int> {
public:
 Test() {
 cout << "T1 and int" << endl;
 }
};
//  Partial reification 
template<typename T2>
class Test<long, T2> {
public:
 Test() {
 cout << "long and T2" << endl;
 }
};
//  Full concretization 
template<>
class Test<long, int> {
public:
 Test() {
 cout << "long and int" << endl;
 }
};
//  Create the above separately 4 A class 
Test<char, char>();
Test<char, int>();
Test<long, char>();
Test<long, int>();

Members of the template

A member template is simply a template within a template


class Base1 { };
class Base2 { };
class Test1 : public Base1 { };
class Test2 : public Base2 { };
template<typename T1, typename T2> 
class Pair {
public:
 T1 t1;
 T2 t2;
 Pair(T1 t1, T2 t2) : t1(t1), t2(t2) {
 }
 //  Member templates in class templates 
 template<typename U1, typename U2>
 Pair(const Pair<U1, U2>& pair) : t1(pair.t1), t2(pair.t2){
 }
};
Pair<Base1*, Base2*>(Pair<Test1*, Test2*>(new Test1, new Test2));

Unless otherwise specified, the above tests were performed under the win10 vs2017 64bit compiler

conclusion

Above is the site to introduce C++ generic programming, I hope to help you!


Related articles: