Analysis of application of C + + template

Source: Internet
Author: User
Tags data structures numeric value strcmp traits

Post the application of the C + + template that you wrote before. Recall at that time in the learning C + + template of helplessness and fear, and now still haunted, I share my experience, only hope that others less detours, in fact, it is so several uses, do not need to be afraid.

I summarized four ways to use the template, basically covering most of the template usage scenarios, knowing these four ways, you can see other code to understand why people use templates in this place.


Four scenarios for templates 1. Generic programming that separates data types from algorithms
2. Type adaptation traits
3. Function forwarding
4. Meta Programming

1. Generic programming that separates data types from algorithms in template programming, the most common is this type of usage, separating data types from algorithms and implementing generic programming.
STL itself realizes the separation of data container and algorithm, and the large number of template application in STL realizes the separation of data type and container algorithm, it is a model of generic programming.
Such as:

Std::vector<int>
std::vector<long>


A single piece of the template implementation, the single piece of the algorithm and the type of single pieces of separation.
Such as:
Template <class t> class  Singleton  
{  
protected:  
    Singleton () {} public  
:  
    static t& getinstance ()  
    {  
        static T instance;  
        return instance;  
    }  
;  
Class cmysingleton:public singleton< Cmysingleton >  

The separation of data types and algorithms is one of the easiest uses to understand, and I think this may be the original intention of inventing generic algorithms.
2. Type adaptation TraitsC + + textbooks will certainly mention the polymorphism of C + + language. My understanding of polymorphism is that the same method produces different behaviors. This is the most common use case in C + + is a virtual function, virtual function Quilt class overridden by subclasses, different subclasses for the same virtual function calls behave differently, but the caller does not care about the specific implementation, it is only for virtual interface call finished. This polymorphism is the Run-time polymorphism. Because it is at run time to know which subclass function is finally called.
In contrast to Run-time polymorphism, a polymorphic form is implemented by means of a template that allows us to use a single generic tag to correlate different specific behaviors: But this association is processed at compile time, which is called static polymorphism by means of a template polymorphism. Take a look at the example below.

Class A1
{
public:void fun ();
};
class A2
{
public:void fun ();    
};
template<typename a> class Cfuninvoker
{public:
	Static void Invoke (* t)
	{t->fun ();    }
}
A1 A1;    
A2 A2;   
Cfuninvoker<a1>::invoke (&A1);
Cfuninvoker<a2>::invoke (&A2);

A1,A2 two classes, all have a fun function. Another caller cfuninvoker needs to invoke the fun function of these two classes. The above example, A1 and A2, have nothing to do with it, they just need to provide a function called the fun parameter is empty can be called. And the caller cfuninvoker to the caller's request that there is such a function on the line. A nearly complete decoupling of a1,a2,cfuninvoker can be achieved by simply agreeing on a good number of functions and parameters.

If implemented using dynamic polymorphism, it requires A1 and A2 to inherit from the same parent class that contains the virtual interface fun (for example, this parent class is called cfunbase). And for Cfuninvoker, it needs to define a parent class pointer (cfunbase*) and call it. At this time, A1 and A2 are not so free, and any changes to cfunbase will affect the functions of A1 and A2. So the coupling of the a1,a2,cfuninvoker is getting higher, and they need a class to implement the association.

Therefore, the benefits of static polymorphism are: static polymorphism does not need to implement polymorphic types with a common base class, because it can be decoupled to some extent, but it still requires some protocol between the template class and the template parameter (here the protocol, for example, requires a function called the fun parameter to be empty).

But if some template parameter types do not meet these covariance, what to do. Like I want to call Cfuninvoker<int>::invoke but the int type does not provide a function named the fun parameter is empty.

so we introduce another use of static polymorphism: Traits (extract)

For example, the following host class requires a template parameter type to provide a method called dosomething, so host<a> can be compiled, but Host<int> is compiled

To solve this problem, we add a traits class, which will certainly provide a dosomething method to the outside world. For ordinary types, it forwards this method, which is special to the int type, and implements an empty DoSomething method. So whether it's host<traits<a>> or HOST<TRAITS<INT>>, you can compile

The traits is used extensively in STL. Like our common string type, do not assume that it can handle only strings, it handles any type, and you can even use it to handle binary buffer (Binaryarray).

For example, we can modify std::string so that it handles the long type internally, making it an array of long arrays.

typedef 
Basic_string<long, Char_traits<long>, allocator<long> > longstring;

Longstring Strlong;
Strlong.push_back (a);
Strlong.push_back (4562);
Long arrlong[2] = {4562};

Longstring Strlongfromarr (Arrlong, ArraySize (Arrlong));
ASSERT (Strlong = = Strlongfromarr);


3. Function forwarding

Many of the application of the template class is that it can generate different classes for different template parameters. This allows us to record the function pointer and its parameter types through the template class, and then call the function when needed.

There are many applications based on function forwarding.
C + + reflection implemented by the C + + delegate template implemented by the Boost::function boost::signal slot template ...


When it comes to storing function pointers for deferred calls, you can apply function forwarding


The following simulates a simple forwarding

Template<typename t> class function;
Template<typename R, TypeName a0>
class function <r (A0) >
{public
:
typedef r (*fun)  (A0);   
function (fun ptr): M_ptr (PTR) {}
R operator () (A0 a)
{(*m_ptr) (a);}  
Fun m_ptr;  
};
int Testfun (int a)
{
printf ("%d", a); return
2;
}
function<int (int) > f1 (&testfun);
F1 (4);
The above example saves the function Testfun function pointer and its function signature int (int) as template parameters in the F1 object. When needed, the function can be invoked with F1.


The following example simulates the forwarding of a class member function

<pre name= "code" class= "CPP" >template<class t> class function;  
Template<typename R, TypeName A0, TypeName t>  
class  Function<r (T::*) (A0) >   
{public  
:  
    typedef R (T::* Fun) (A0);  
     function (fun p, t* pthis): M_ptr (P), M_pthis (pthis) {}  
  
    R operator () (A0 a)      {(m_pthis->*m_ptr) (a);}  
    Fun m_ptr;  
    t* m_pthis;  
};  
Class CA
{public
:
	void Fun (int a) {cout << A;}}
;

CA A;
Function<void (CA::*) (int) >   f (&ca::fun, &a);
F (4);  Equivalent to A. Fun (4);

The example above puts the class CA's object pointer, member function pointer, and its member function signatures

void (CA::*) (int)
is stored as a template parameter in the object F. When needed, this member function function of this object can be invoked with F.

The way it's called is simple.

F (4);  Equivalent to A. Fun (4);
It's like calling a normal C function. The CA class object is missing,. Or the-> operator is missing. function forwarding implements a layer of encapsulation and binding, and ultimately the caller is isolated from the CA type to realize decoupling.



However, this encapsulation of function forwarding makes the invocation less efficient, and how to make the encapsulated call as fast as a normal function call, please refer to another learning experience I sent

The principle of efficient C + + delegate

4. Meta Programming Many book introduction metaprogramming is this: Metaprogram:a program about a programs. Is "a program about another program." This is a lot of introductions. Start with an example: A program that accumulates from 1 to 100
The main template has an integer parameter n, and the value of the enumeration value in the main template takes the values of the template class that the template parameter is N-1, plus its own n value. Then special processing value=1 for n=1. So in the Getsum<100> class, its value is 5050. This value is not computed at runtime, but compiled at compile time. This long C + + code eventually compiles the result and writes only one sentence

Prinft ("%d", 5050);

The resulting assembly instructions are the same.
From this small example can be summed up the idea of metaprogramming: The calculation of a type or numeric value is implemented at compile time.
Using the template special mechanism to implement the compile-time conditional selection structure, the recursive template is used to implement the compile-time loop structure, and the template-element program is interpreted by the compiler at compile time.


the tools that we can use to help compute at compile time are:
Template's special function overload resolution typedef static type variables and functions sizeof, =,:?,-,+,&LT, > Operators enum
1. The special usage of meta programmingGeneral use of special conditions to achieve the judgment.
Including the judgment of the ordinary if
Judging the end of cyclic conditions
。。。。。
Here is an example
struct is_void
{
enum{value = false;}  
}  
template<>
struct is_void<void>
{
enum{value = true;}  
}  
std::cout << is_void<int>//Display False

The above example can be used to determine whether a type is a void type
2. Function overload resolution usage in metaprogrammingThe following example comes from the new thinking of C + + design patterns to determine whether there is a transformational relationship between two types
<pre name= "code" class= "cpp" >template <class T, class u> struct
conversion
{
static char T  EST (U);  
static Long Test (...);  
static T maket ();  
enum {exists =
(sizeof (Test (maket ()) = = sizeof (char))};  
};


class A;
Class B:public A;

printf ("%d,%d", conversion<b*, A*>::exists, conversion<a*, b*>::exists);
                    Output 1,0
The above example obtains the return value of the overloaded function test using overloaded resolutions and sizeof, and then saves it at compile time by enumerating the constant exists. In
conversion<b*, a*>
, the overload resolution uses the Char Test (A *) method, so conversion<b*,a*>::exists is 1.
And in
conversion<a*, b*>
, the overload resolution takes a long Test (...) method, so the conversion<a*,b*>::exists is 0.
3. typedef usage in Meta programmingIn Metaprogramming, a typedef is primarily used to form a compile-time type data structure.
The most classic typelist structure
Boost::tuple structures are also based on similar typelist structures.
Boost's MPL library also implements data structures such as vector map set

For example, the following example implements a CLASSA=&GT;CLASSB=&GT;CLASSC type list.
typedef struct null_type{} nulltype
template<typename T, typename U = nulltype> struct
t  Ypelist
{
typedef T head;  
typedef U Tail;  
}  
typedef typelist<classa, typelist< CLASSB, typelist< classc,   Nulltype>>> mytypelist;

This type of linked list is only type information, what is the use of it. We can change it, add two objects to it, form a list of objects that can store different types of elements in a linked list.
Template<typename T, typename U = nulltype>
struct typelist
{
typedef t head;  
typedef U Tail; Head

m_head;
Tail  m_tail;
}

Typelist<classa, typelist <ClassB>> storage;
Storage. M_head = ClassA ();
Storage.m_tail.m_head = CLASSB ();

This list saves two instance objects of ClassA and CLASSB.
Here's an example of using Typelist's powerful powers.

typelist implement Simple Factory
Most of the time we'll be involved in object factories, which is one of the simple factory patterns that produce different class objects based on requirements. Here is an example of a simple factory, which is ubiquitous in a variety of C + + projects.
void * Createobj (const std::string & strclsname)  
{  
    if (strclsname  = = "ClassA")  
    {return  
        new  ClassA ();  
    }  
    else if (strclsname  = = "ClassB")  
    {return  
        new  classb ();  
    }  
    else if (strclsname  = = "CLASSC")  
    {return  
        new  classc ()  
}}

This is a branching structure, and if the type is particularly numerous, the code can be very long and very lame. We can use Typelist to help us generate such code. It's a tall method.
Class ClassA {   public; virtual const char*  getclassname () {   return  m_classname;} stat IC char* m_classname;
Each type uses a string to represent its own type};


char* classa::m_classname = "ClassA"; Class CLASSB ... class classc ... typedef        Typelist<classa,          & nbsp  Typelist< CLASSB                typelist< classc>    &NB Sp       >       >       mytypelist;  template<typename t Ypename u>    struct typelist      {       typedef T head;       typedef U Tail;            static void* creatobj (const char *pname)          {&nbs p;          if (strcmp (head::m_classname, pname) = 0)            {     &NBSP;         return to new head;  //find the corresponding class        }              else      &nbs P       {               return tail::creatobj (pname);/This is the Typelis.


T makes recursive calls, resulting in branch code            {}       }  ; Template<typename t>    struct typelist<t, Nulltype >//Special for recursive end conditions {      & nbsp   Static void* creatobj (const char *pname)          {         &NBS P if (strcmp (head::m_classname, pname) = 0)            {         & nbsp     return to new head;           {             else        &NBSP ;     {               REturn NULL;


          }       }  ;
classa* pa = (classa*) mytypelist:: Creatobj ("ClassA");
classb* PB = (classb*) mytypelist:: Creatobj ("CLASSB");

classc* pc = (classc*) mytypelist:: Creatobj ("CLASSC");
 ...

This approach does not reduce the number of if else in the final assembly instruction level, but it does not require us to write so many if else. Through the recursive mode of the template, let the compiler automatically generate branch judgments for us.
Dynamic type creation has been implemented by the previous class factory, and now we can implement dynamic type recognition in a similar way RuntimeClass has two main functions:
1. Dynamic type creation
classa* pobj = createobj ("ClassA");
2. Dynamic type recognition
Pobj->iskindof (Classa::getruntimeclass ());

In MFC, the IsKindOf method determines whether it belongs to a type by traversing the chain of inheritance. As soon as we see this kind of traversal or loop, we can consider using module recursion to realize
Here is the implementation code. Just use the conversion template mentioned earlier
Template<typename T,   typename   U = nulltype>    struct typelist      { &
nbsp  typedef T Head;    typedef U Tail;       template<typename superclass>    static bool IsKindOf (const char *pname)       {     If strcmp (head:: GetClassName (), pname) = = 0)      {&nbs p;     return conversion 


A lot of metaprogramming technology, such as numerical operations (the simplest 1 to 100 cumulative example), I am here just exhaustive, specific reference to the "C + + design mode of new thinking."

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.