about templates & generics programming issues in C + +:
Question introduction: How to write a universal addition function.
(1) Use a function overload to recreate it for each of the different types of behavior required
int Add (const int &_ileft, const int&_iright)
{
Return (_ileft +_iright);
}
float Add (const float &_fleft, constfloat &_fright)
{
Return (_fleft +_fright);
}
Shortcomings
1, as long as a new type appears, you need to add the corresponding function.
2, except the type, all functions of the function body are the same, the code of the reuse rate is not high
3, if the function simply returns the value type is different, the function overload cannot solve
4, a method has problems, all the methods have problems, not maintenance.
(2) Use common base classes to place common code in a common base class
Shortcomings
1, the use of common base classes to write common code, will lose the advantages of type checking;
2, for many later implementation of the classes, must inherit from a particular base class, code maintenance more difficult.
(3) Use of special preprocessing procedures
#define ADD (A, B) ((a) + (b))
Shortcomings
Not a function, no parameter type detection, security is not high
To sum up, we need to introduce generic programming, which is to write a template for the function or class that is needed, and to instantiate it when it is practical. So, what is generic programming. What is a template.
first, generic programming
Generic Programming : Writing logical code that is not type-independent is a means of code reuse. Templates are the basis for generic programming.
Second, function template
function Template: represents a function family, which is independent of the type, is parameterized when used, and produces a specific type version of the function based on the argument type.
format of template function definition : Template<typename T1, Teypename T2, ... typename tn>
function return value function name (argument list)
{
. . . . . .
}
Eg:
Template<typename t>
T Add (t left, t right)
{
return left+right;
}
Template and TypeName are keywords, T is the name of template parameter, can be named arbitrarily.
TypeName is used to define a template parameter keyword, or you can use class. The proposal tries to make TypeName.
instantiation : A template is a blueprint that itself is not a class or a function, a compiler creates a specific type of class or function with a template, and a process that produces a specific type of template called a function template instantiation
Note: The template has been compiled two times:
Before ① instantiate, check the template code itself to see if there are any syntax errors, such as: omitting a semicolon
② during instantiation, check the template code to see if all calls are valid, such as: The instantiation type does not support some function calls
argument deduction:
The process of determining template parameter types and values from function arguments is called template argument inference, and arguments for multiple type parameters must match exactly
type parameter conversions:
Instead of converting the argument to match an existing instantiation, a new instance will be generated.
The compiler performs only two transformations:
1. Const conversion: A function that receives a const reference or a const pointer can be invoked separately with a reference to a non-const object or a pointer
2, array or function to the pointer conversion: If the template parameter is not a reference type, the arguments of an array or function type apply a general pointer conversion. Array real arguments as a pointer to its first element, which is used as a pointer to a function type.
Eg:
Template<typename t>
void FunTest1 (const t* T)
{
cout<< "FunTest1 ();" <<*t<<endl;
}
Template<typename t>
void FunTest2 (const t& T)
{
cout<< "FunTest2 ();" <<t<<endl;
}
Template<typename t>
void FunTest3 (t t1, T T2)
{
cout<< "FunTest3 ()" <<endl;
}
int Add (int a, int b)
{
return a+b;
}
int main ()
{
int A = 10;
int* PA = &A;
FunTest1 (PA);
int B = 20;
int& PB = b;
FunTest2 (PB);
int array1[10];
int array2[20];
FunTest3 (Array1, array2);
FunTest3 (Add,add);
System ("pause");
return 0;
}
Template parameters: function templates have two types of parameters: template parameters and call parameters.
(1) Template parameters:
A, template parameter names can be used only after template parameters to the end of the template declaration or definition, following the name masking rules.
b, the name of the template parameter can only be used once in the same template parameter list.
Template<typename T, typenamet>
Void funtest (t t1, T T2)
{};
c, all template parameters must precede class or TypeName keyword modification, and two keywords can be mixed.
D, the default template arguments cannot be specified within the function template.
Template<typename t>
T ADD (t = int, t)//Compile error.
{};
non-template type parameters :
Non-template type parameters are constants defined internally by the template, and you can use non-template type arguments when you need a constant expression.
Note:
1, the template parameter list uses <> to enclose.
2, and the function parameter table, with multiple parameters must be separated by commas, types can be the same or different.
Template<typenamet, TypeName V .......>
3, template parameter list can not be empty (template specificity, template parameters are empty).
4. Template parameters can be type parameters or non type new parameters, followed by class and TypeName
5. A template type parameter can be used as a type description used anywhere in the template, in exactly the same way as a built-in type or custom type, to specify function parameter types, return values, local variables, and coercion type conversions.
6, template parameter list, class and TypeName have the same meaning, can be exchanged, use typename more intuitive. But the keyword TypeName is added to C + + as the C + + standard, and the old compiler may not support it.
template function Overload:
Note:
1, the Declaration of all overloaded versions of the function should precede the function being called
2, a non-template function can exist with a function template with the same name, and the function template can also be instantiated as this template function.
3. For non-template functions and function templates with the same name, if all other conditions are the same, the non-template function will be transferred preferentially without generating an instance from the template. If the template can produce a function that has a better match, then the template is selected.
4. Explicitly specify an empty template argument list that tells the compiler that only the template can match the call, and that all template parameters should be interpreted according to the argument.
5. Template functions do not allow automatic type conversions, but normal functions can be automatically typed.
New issues are introduced:
string S1 = "Addfhgj";
String s2 = "ADDFGHJKL";
Max (S1,S2); Failed to deduce template parameters from "Const std::string" to "Const std::move_iterator<_ranit> &".
Therefore, the template has some special circumstances can not be processed, you need to introduce the special template, what is the special template.
template function specificity:
Sometimes it is not always possible to write a template that is most appropriate for all types that may be instantiated, and in some cases a generic template definition may be completely wrong for a type, or it will not compile, or do something wrong.
Eg: Compare the size of two strings
After debugging, it is found that the size of the string address rather than the size of the string is compared. Therefore, template functions need to be specialized to handle special situations. This requires processing the special case of the template class-------template specificity:
Attention:
In a call to a template-specific version, the argument type must exactly match the formal parameter type of the special version function, and if it does not, the compiler instantiates an instance for the argument template definition.
After the invocation of the template instance is not present, the template should include a templated declaration in the header file, and then include the header file with each source file for that specific version.
third, template class
1, the template class definition format:
Template<class parameter name 1, class parameter Name 2, ... class parameter name n>
Class name
{ ... };
Implementing a dynamic sequential table using template methods
Template<typename t>
Class Seqlist
{
Public:
Seqlist ();
~ seqlist ();
Private:
int _size;
int _capacity;
T* _data;
};
Template <typename t>
Seqlist <t>:: Seqlist ()
: _size (0)
, _capacity (10)
, _data (new t[_capacity])
{}
Template <typename t>
Seqlist <t>::~ seqlist ()
{
delete [] _data;
}
void Test ()
{
seqlist<int>s1;
seqlist<double>s2;
seqlist<char>s3;
}
2. Instantiation of template class
As long as there is a different type, the compiler instantiates a corresponding class.
Seqlist<int > SL1;
seqlist<double > SL2;
When defining the two types of sequential tables, the compiler replaces the template parameters with int and double, rewriting the Seqlist class, and finally creating a class named Seqlist<int> and Seqlist<double>.
(1) The template parameter implements the container adapter.
#include "List.h"
Bottom Use list container
Template <class T, class container = list<t>>//Template parameters
Class Queue
{
Public
void push (const t& x)
{
Return _con. Pushback (x);
}
void Pop ()
{
Return _con. Popfront ();
}
Const t& GetHead ()
{
Return _con. Front ();
}
Const t& GetTail ()
{
Return _con. Back ();
}
BOOL IsEmpty ()
{
Return _con. Empty ();
}
Private
Container _con;
};
void Test2 ()
{
queue<int> q1;//Use default template parameters to construct objects
Queue<int, list<int>> q2;//constructs objects using template parameters
}
(2) Template parameters implement the container adapter.
Template <class T, template<class> class container = list>//Use template parameters
Class Queue
{
Public
void push (const t& x)
{
Return _con. Pushback (x);
}
void Pop ()
{
Return _con. Pop ();
}
Const t& GetHead ()
{
Return _con. Front ();
}
Const t& GetTail ()
{
Return _con. Back ();
}
BOOL IsEmpty ()
{
Return _con. Empty ();
}
Private
Container<t> _con;
};
void Test1 ()
{
queue<int>q1;//constructs an object using the template parameters of the default template class
queue<int,list> q2;//Use template parameters to construct different types of objects
}
(3) Non-typed class template parameters
Template <typename T, size_t max_size = 10>//with default template parameters
Template<typename T, double max_size = 10.0>//double: type of non-type template parameter ' max_size ' illegal
Class Array
{
Public:
Array ();
Private:
T _array [max_size];
int _size;
};
Template <typename T, size_t max_size>
Array <t,max_size>::array ()
: _size (0)
{}
void Test ()
{
Array<int> A1;
Array<int, 20> A2;
}
(4) The Special of class template:
Partial implementations of sequential table classes
Template <typename t>
Class Seqlist
{
Public:
Seqlist ();
~ seqlist ();
Private:
int _size;
int _capacity;
T* _data;
};
Template<typename t>
Seqlist <t>:: Seqlist ()
: _size (0)
, _capacity (10)
, _data (new t[_capacity])
{
cout<< "Seqlist<t>:: Seqlist ()" <<endl;
}
Template<typename t>
Seqlist <t>::~ seqlist ()
{
Delete[] _data;
}
Full-specificity:
Template <>
Class Seqlist <int>
{
Public:
seqlist (int capacity);
~ seqlist ();
Private:
int _size;
int _capacity;
Int* _data;
};
The template parameter list is no longer required for defining member functions and member functions after the special
Seqlist <int>:: seqlist (int capacity)
: _size (0)
, _capacity (capacity)
, _data (new int[_capacity])
{
cout<< "Seqlist<int>" <<endl;
}
Seqlist <int>::~ seqlist ()
{
Delete[] _data;
}
void Test1 ()
{
seqlist<double > SL2;
Seqlist<int > SL1 (2);
}
Partial specificity (partial specificity)
Template <typename T1, TypeName t2>
Class Data
{
Public:
Data ();
};
Template <typename T1, TypeName t2>
Data<t1, T2>::D ATA ()
{
cout<< "Data<t1,t2>" <<endl;
}
Local specificity The second parameter is a specific type, such as int, double, etc.
Template <typename t1>
Class Data <t1, int>
{
Public:
Data ();
};
Template <typename t1>
Data<t1, Int>::D ATA ()
{
cout<< "Data<t1,int>" <<endl;
}
As can be seen in the following example, the partial specificity does not merely refer to the specific part of the parameter, but is a special version designed for the further conditional restriction of the template parameter.
Local specificity two parameters are pointer types
Template <typename T1, TypeName t2>
Class Data <t1*, t2*>
{
Public:
Data ();
};
Template <typename T1, TypeName t2>
DATA<T1 *, T2*>:: Data ()
{
cout<< "Data<t1*,t2*>" <<endl;
}
Local specificity two parameters as references
Template <typename T1, TypeName t2>
Class Data <t1&, t2&>
{
Public:
Data (const t1& d1, const t2& D2);
};
Template <typename T1, TypeName t2>
Data<t1, T2&>:: Data (const t1& d1, const t2& D2)
{
cout<< "Data<t1&,t2&>" <<endl;
}
void Test ()
{
Data<double, int> D1;
Data<int, double> D2;
Data<int *, int*> D3;
Data<int&, Int&> D4 (1, 2);
}