Analysis of C + + template application

Source: Internet
Author: User
Tags strcmp traits

Published the application of the C + + template once written.

Recalling the helplessness and fear of learning C + + templates at the time, there is still a horror. I share my experience, just hope that others less detours, in fact, it is so many ways to use, do not need to be afraid.

I have summed up the template four ways to use, basically covering most of the template usage scenarios, understand the four ways. You will be able to see other code to understand why people use templates in this place.


Four scenes of the template1. Generic programming for separating data types from algorithms
2. Type adaptation traits
3. Function forwarding
4. Meta-programming
1. Generic programming for separating data types from algorithmsIn template programming, this is the most common way to use this class. The data type is separated from the algorithm for 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, which is a model of generic programming.
Such as:

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


A single-piece template implementation that separates the single-piece algorithm from the single-piece type.
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 from algorithms is one of the easiest to understand usage scenarios. I think this may also be the original intention of inventing the generic algorithm.


2. Type adaptation traitsc++ textbooks will certainly mention the polymorphism of the C + + language. My understanding of polymorphism is that the same approach produces different behaviors. This is the most common use case in C + + is virtual function, virtual function Quilt class overridden by subclasses, different subclasses for the same virtual function calls show different behavior, but the caller does not care about the detailed implementation, it is only for the virtual interface to call the finished.

This polymorphism is the polymorphism at execution time. Since it is executed, it is not known which subclass function is finally called.


Compared with the execution time polymorphism, another form of polymorphism is achieved by means of a template. The template agrees that we use a single generic tag to correlate different specific behaviors: But such associations are processed at compile time, which is called static polymorphism with the aid of template polymorphism. Take a look at the demo sample below.

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

A1,a2 of two classes. All have a fun function. There is also one caller cfuninvoker need to invoke the fun function of these two classes.

The example above. A1 and A2 do not have any connection, they only need to provide a function called "fun" can be called. The caller, Cfuninvoker, has a function that is required for the callee. The A1,A2 can only be achieved by means of an agreement on the function name and the number of parameters. The Cfuninvoker almost completely decoupled.

Assuming that the dynamic polymorphism is implemented, it is necessary that A1 and A2 inherit from the same parent that contains the virtual interface fun (for example, the parent class is called cfunbase). And for Cfuninvoker. It needs to define a pointer to this parent class (cfunbase*). And make a call to it. At this time, A1 and A2 are not so free. Any changes to Cfunbase will affect the functionality of A1 and A2. So A1. The coupling of the a2,cfuninvoker has become higher. What they need is a class to implement the association.

Thus, the advantage of static polymorphism is that static polymorphism does not require the realization of polymorphic types with a common base class. Because it can be decoupled to some extent. But it still needs some protocol between the template class and the template reference (the protocol here is, for example, a function called "empty" in the sample above).

But suppose some template parameter types do not meet these semantics. What to do? For example, I want to call Cfuninvoker<int>::invoke, but the int type does not provide a function named fun with a null parameter.

So we're introducing static polymorphism, and there's another use: Traits (the extract)

For example, the following host class requires a template parameter type to provide a method called dosomething. So host<a> is able to compile through, but Host<int> is compiled just for the

watermark/2/text/ahr0cdovl2jsb2cuy3nkbi5uzxqvy3l4axnncmvhda==/font/5a6l5l2t/fontsize/400/fill/i0jbqkfcma==/ Dissolve/70/gravity/southeast "style=" font-size:18px ">

In order to solve the problem. We add a traits class that will certainly provide a dosomething method to the outside. For normal types, it forwards this method, which is specific to the int type. An empty DoSomething method is implemented. So whether it's host<traits<a>> or HOST<TRAITS<INT>>, it can be compiled

Traits are used extensively in STL. For example, our common string type, do not think that it can only handle strings, it can handle whatever type, you can even use it to handle the binary buffer (Binaryarray).

For example, we can change std::string to have it handle the long type internally, making it a long array.

typedef basic_string<long, Char_traits<long>, allocator<long> > Longstring;longstring strlong; Strlong.push_back; Strlong.push_back (4562); long arrlong[2] = {, 4562};longstring Strlongfromarr (ArrLong, ARRAYSIZE (Arrlong)); assert (Strlong = = Strlongfromarr);


3. Function forwarding

A very many application of template classes 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. Call the function again when it is needed.

There are a lot of applications based on function forwarding

    • Boost::function
    • Boost::signal slot
    • Template implementation of C + + entrusted
    • C + + reflection of template implementations
............


Usually involves storing the function pointer. In case of deferred invocation, function forwarding can be applied


The following simulates a simple forwarding

Template<typename t> class Function;template<typename R, TypeName a0> class function <r (A0) >{Publi      C: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 pointer of the function Testfun and its function signature int (int) as the template parameter in the F1 object. You can call this function with F1 when you need it.


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);  is equivalent to a. Fun (4);

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

void (CA::*) (int)
It is saved as a template parameter in the object F. When needed, it is possible to invoke this member function function of this object with F.

Call the way very easy

F (4);  is 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. Finally, the caller is isolated from the CA type to realize decoupling.



Just such encapsulation of function forwarding will make the call less efficient. How to make a post-encapsulation call as fast as a normal function call, please refer to my post and I have a learning experience

Principles of efficient C + + Trust

4. Meta-programming many books introduce meta-programming to say this: Metaprogram:a program. Is "a program about another program." There is much to be described in this regard.

Start with a demo sample: A program that accumulates from 1 to 100
The main template has a number of shaping parameters N. The value value of the enumeration value in the main template takes the values of the template class with the template number N-1, plus its own n value.

And then for n=1 when the special processing value=1.

So in the Getsum<100> class, its value is 5050. This value is not computed at the time of execution, but the compiler has already done so at compile time. So long C + + code finally compiled the results and just write a sentence

Prinft ("%d", 5050);

The resulting assembly instructions are the same.
From this sample can be summed up the idea of meta-programming: 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 realize the compile-time loop structure, and the template meta-Program is run by the compiler during the compilation period.



The tools we can use to help with calculations at compile time are:

    • The specificity of the template
    • Function overload resolution
    • typedef
    • Static type variables and functions
    • sizeof
    • =,:?. -。 +,<, > operator
    • Enum

1. How to use special methods in meta-programmingGeneral use of specificity to achieve the inference of conditions.
Inference that contains ordinary if
Cyclic conditional termination Inference

。。


Here is a sample

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 infer whether a type is void type
2. How to use function overload resolution in meta-programmingHere's a sample from the C + + design mode new thinking to infer whether there is a conversion relationship between two types
<pre name= "code" class= "CPP" >template <class T, class u> struct Conversion {static char Test (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 uses overload resolution and sizeof to obtain the size of the return value of the overloaded function test, which is then saved by enumeration constants exists at compile time. In
conversion<b*, a*>
, the overload resolution uses a char Test (A *) method, so conversion<b*,a*>::exists is 1.
And in
conversion<a*, b*>
, the overload resolution uses a long Test (...) method, so conversion<a*,b*>::exists is 0.
3.

typedef used in meta-programming in meta-programming, typedef is mainly used to form a compile-time type data structure.
The most classic typelist structure
Boost::tuple structures are also based on similar typelist structures.
Data structures such as vector map set are also implemented in the Boost MPL library.

For example, the following demo sample implements a CLASSA=>CLASSB=>CLASSC type list.

typedef struct null_type{} nulltype template<typename T, typename U = nulltype> struct Typelist {   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 transform it, add two objects to it, and 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 lists the two instance objects of ClassA and CLASSB.
Here's an example of using Typelist's powerful Viagra.

Typelist to implement a simple factory
Very often we relate to object factories, which is one of the simple factory models that produce different classes of objects based on demand. Here is an example of a simple factory. Such code 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 the code can be very long and very lame assuming the type is so much more. We can use Typelist to help us generate this code.

It's a tall way.

Class classa{  public;virtual const char*  getclassname () {   return  m_classname;} Static 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,            Typelist< ClassB,               typelist< classc>           >      >     mytypelist  template<typename T, TypeName u>   struct Typelist   &  nbsp; {      typedef T Head;      typedef U Tail;          Static void* creatobj (const char *pname)         {        &NBSP ; if (strcmp (head::m_classname, pName) = = 0)           {        &NB Sp    return new Head;  //find the appropriate class        }             else             {              return tail::creatobj (pName);//This is the Typelist A recursive call was made. resulting in branch code          }      }  };template<typename t>    struct typelist<t, Nulltype >//Special for recursive end condition {        static void* creatobj ( const char *pname)         {          if (strcmp (head::m_classname  , pName) = = 0)           {              return new Head;         }             else         & nbsp   {              return NULL;          }      }  }; classa* pa = (classa*) mytypelist:: Creatobj ("ClassA"); classb* PB = (classb*) mytypelist:: Creatobj ("ClassB"); classc* pc = (classc*) mytypelist:: Creatobj ("ClassC");

Such a way does not degrade the number of if else at the assembly instruction level that is finally generated. But it doesn't require us to write so many if else. Let the compiler take the initiative to generate branching inferences for us through the recursive way of the template.
Dynamic type creation has already been implemented by the previous class factory, and today we are able to implement dynamic type recognition with similar aspects 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 is to determine whether it belongs to a type by traversing the chain of inheritance. A way to see such a traversal or loop. We'll be able to consider the recursive implementation of the module.
The following is the implementation code. Just use the conversion template you mentioned earlier
Template<typename T,   typename   U = nulltype>   struct typelist     {&NBSP;&NB Sp; typedef T Head;   typedef U Tail;     template<typename superclass>   static bool IsKindOf (const char *pName)      {    if (strcmp (Head:: GetClassName (), pName) = = 0)     { & nbsp;   return conversion

There are many meta-programming techniques. For example, there are numerical operations and so on (the simplest 1 to 100 cumulative sample), and here I am just exhaustive. In detail, we can refer to the new thinking of C + + design mode.


Analysis of C + + template application

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.