C ++ generic programming: Source initiation, implementation, and Significance

Source: Internet
Author: User

C ++ generic programming: Source initiation, implementation, and Significance
Why generic
The motivation for the initial introduction of generic programming is simple and straightforward: the invention of a language mechanism can help achieve
Common standard container library. The so-called general standard container library is to be able to do so, for example, using a list class to store all possible types
Object, such a thing; people familiar with other object-oriented languages should know, such as Java, this is stored in the list
Object reference.
Java's single inheritance plays a key role here.
However, a single inheritance of c ++ is in the language chain.
The underlying language is unacceptable. In addition, using a single inheritance to implement generic containers also brings about efficiency and type security issues,
Both of them are not consistent with C ++'s philosophy.
Therefore, C ++ seeks another method-in addition to a single inheritance, another solution for implementing General containers is to use the "parameterized type ". One container
To store any type of objects, simply extract the object type and parameterize it [1]:
Template <class T> class vector {
T * V;
Int SZ;
Public:
Vector (INT );
T & operator [] (INT );
T & ELEM (int I) {return V [I];}
//...
};
Generally, when we see this definition, everyone will think of the macro of C. Indeed, templates and macros are similar in spirit. And
Indeed, some people use the macro of C to implement general containers. The template is to parameterize the types in a definition, and the macro can also achieve Parameters
Data type. In a sense, we can say that a macro is the superset of a template-because a macro can not only be parameterized, but also can be parameterized.
All text,
Because it is a text replacement tool.
However,
Compared with the template,
The biggest drawback of a macro is that it does not work in C ++.
In terms of Syntax Parsing, macros are processed by the pre-processor, but in the eyes of the pre-processor, there is no C ++, there is only a bunch of text, so c ++'s
Type check does not work at all. For example, if the above definition is implemented using a macro, even if the t you pass in is not a type, preprocessing
No error will be reported. A bunch of inexplicable type errors will be found only when the text is replaced and the C ++ compiler is working,
At that time, errors were everywhere. At last, a bunch of scary compilation errors are thrown. Moreover, Acer cannot be debugged.
Note 1
In fact, there is also a way to implement general containers. But it is worse: it requires that any type that can be stored in the container inherit from one
Nodebase,
Nodebase has pre and next pointers,
In this way,
You can chain any type into a linked list.
But the fatal disadvantage of this method is that (1) it is invasive, and each type that can be placed in the container must inherit from the nodebase base class.
(2) It does not support basic built-in types (INT, double, etc.), because the built-in types are not and cannot inherit from nodebase. This is not enough.
It is of Type insecurity and efficiency problems.
Let's take a look at general algorithms. This is another motive for generics. For example, the familiar qsort of C:
Void qsort (void * base, size_t nmemb, size_t size,
INT (* COMPAR) (const void *, const void *));
This algorithm has the following problems:
1.
Type security: You must ensure that the element type of the array to which the base points is consistent with that of the COMPAR parameter;
You must ensure that the size must be the size of the array element type.
2.
Versatility: qsort has strict requirements on the binary interface of the parameter array-it must be a memory continuous array. If you implement
A clever, piecewise continuous custom array, you can't use qsort.
3.
Interface intuition: if you have an array char * arr = new arr [10], the element type of the array is actually "disclosed ".
Your size. However, qsort dropped the element type of the array to "Void" (void * base), so this information was lost, and only
The caller manually provides a size. Why should I declare the array type as void *? Because there is no other way to declare it as any class.
Type pointer is incorrect (the same is true for the COMPAR parameter type ). For the sake of universality, qsort discards the type information, which leads
Additional parameters must be used to provide type size information. The problem is not obvious in this particular algorithm. After all, there is only one more size parameter,
However, once more type information is involved, scalability and intuition problems of its interfaces will gradually become apparent.
4.
Efficiency: COMPAR is called through the function pointer, which brings about a certain amount of overhead. However, this problem is not the same as other problems mentioned above.
Is the most serious.
Generic programming
Generic programming was originally born in C ++ and was created by Alexander Stepanov [2] and David musser [3. The objective is to implement C ++
STL (standard template library ). The language support mechanism is templates ). The template spirit is actually very simple: parameterized type. Change
In other words, extract the type information of an algorithm or class that is originally specific to a specific type and extract it into template parameter T. For example, qsort
After generalization, it becomes:
Template <class randomaccessiterator, class compare>
Void sort (randomaccessiterator first, randomaccessiterator last,
Compare comp );
Among them, the first and last iterators represent a forward closed and backward open interval. The iterator and the forward open and backward closed intervals are the core concepts of STL. Iteration
The built-in pointer interfaces (such as unreference, increment, and decrease) and the forward, open, and backward closed intervals are used as a simple mathematical concept.
All elements in the range (including first) to last (excluding last. In addition, comp is a functor function ). Similar functions
The core concept of STL. The Imitation function is the interface of the built-in modeling function. A imitation function can be either a built-in function or a duplicate function.
Class objects carrying operator () can be converted into a function as long as they support the syntax form of function calls.
Through Operator overloading, C ++ allows the user-defined type to have the same interface as the built-in type.
Mechanism, C ++ allows an algorithm or class definition, and can use this interface consistency to generalize itself. For example
Built-in Pointer algorithms are generalized into algorithms that manipulate all iterators. An algorithm that originally used built-in function pointers is generalized
The algorithm that accepts all class objects that have been overloaded with the function call operator.
Let's take a look at how the template solves the problems mentioned above in qsort:
1.
Type security: If you call STD: Sort (ARR, arr + N, comp), the comp type must be the same as the array element type of arr.
Otherwise, the compiler will help you check the consistency. In addition, the parameter type of comp no longer needs the inintuitive representation of const void,
Instead, it can be directly declared as the type of the corresponding array element.
Versatility: I have already said this. One of the core purposes of generics is universality. STD: Sort can be used in all iterators.
2.
The Compare function can be any object that supports the function call syntax. If you want to use STD: sort on your own container
As long as you define an iterator class of your own (strictly speaking, it is a random access iterator, STL classifies the iterator's access capabilities,
Random Access iterator provides built-in pointer access capabilities for modeling. If necessary, define your own function class.
Intuitive interface: Compared with qsort, STD: sort does not have anything to do with the interface, nor does it have an intuitive size parameter. One has
3.
The interval to be sorted. A quasi-function that represents a relatively standard value. This is nothing more than [4].
Efficiency: If the Compare function that you pass to STD: sort is a UDF with a custom operator. Then the compiler can
4.
Use the type information to call operatpr () of the function. Eliminate the function call overhead.
Dynamic and Static Polymorphism
The core activities of generic programming are Abstraction:
Abstract The types of irrelevant commonalities in an algorithm that is specific to some types,
For example, STL
In
In the concept system, whether you are an array or a linked list, it is always a range, which is an abstraction layer. You are a built-in
A function is also a custom class. It is always a callable (callable) object (represented in C ++ through a function imitation ).
Is a layer of abstraction. The process of generic programming is a process of constantly improving these abstractions (LIFT). The ultimate goal is to form
A general algorithm or class to the maximum extent.
Some people will certainly ask, since the same is abstract, why not use multi-state object-oriented abstraction? For example, STD: for_each of STL, use
Java's polymorphism mechanism can also solve the following problems:
Interface iunaryfun
{
Void invoke (Object O );
}
Interface iinputiter
{
Iinputiter preincrement ();
Boolean equals (iinputiter otheriter );
... // Other methods
}
Iunaryfun for_each (iinputiter first, iinputiter last, iunaryfun func)
{
For (;! First. Equals (last); first. preincrement ())
Func. Invoke (* First );
Return func;
}
In fact, the main reason here is simple and efficient. Object-oriented polymorphism introduces indirect calls. Of course, this does not mean that indirect calls are not good,
Sometimes, for example, when the Runtime polymorphism is required, we can only resort to the method of inheritance. However
Why do we have to pay unnecessary indirect call overhead? For example, if for_each is used to implement its versatility
Abstract action penalty ). The C ++ template aims to eliminate such abstract punishments. Use template Encoding
The written STD: for_each has an independent and most efficient instantiated version for each combination of specific parameter types.
A special for_each algorithm specific to these types is the same [5].
So the abstract punishment disappears,
This is exactly what the C ++ template library can do.
One of the important reasons why the industry is widely used in areas where c ++ is best at (areas that focus on efficiency.
On the other hand, the code space is increased when each set of parameter types are combined to instantiate a version. This is a typical spatial
For example, for a language that is static and pursuing efficiency, the overhead of this code space is also indispensable because
Even if you manually write a specific algorithm version for a combination of different parameter types, the Code overhead is the same, and
It also violates the dry principle [6]. In addition, because you do not need to always think about the interfaces to be created during abstraction, generic algorithms are compiled.
It is also more intuitive.
Another benefit of C ++ generics is that C ++ templates are non-
Invasive. You do not need to let your class inherit from a specific interface to be used for an algorithm. You only need to support a specific syntax interface.
(For example, you only need to support the begin () call ). This is also called structural conformance, meaning that as long as the syntax structure
Consistent. On the other hand, object-oriented polymorphism Based on interface inheritance must be explicitly declared to inherit from an interface. This is called
Named conformance ).
Of course, the static polymorphism supported by generics and the dynamic polymorphism supported by virtual functions do not have any absolute advantages or disadvantages. They apply to different
. When the type information is available, the maximum efficiency and flexibility can be achieved through the compilation phase polymorphism. When the specific type information is not available
You must resort to the Runtime polymorphism. Bjarne stroustrup once used a typical example to clarify the difference:
STD: vector <shape *> V;
... // Fill v
STD: for_each (V. Begin (), V. End (), STD: mem_fun (& shape: Draw ));
Here, what kind of shape will be stored in V in the future, which cannot be known during compilation, so dynamic polymorphism is necessary. On the other hand,
The compiler does know that they all inherit from the shape. With this only static type information, we use the generic algorithm STD: for_each.
And generic container STD: vector. It is particularly worth noting that for_each's static polymorphism behavior: for_each has only one template implementation,
However, the for_each action varies depending on the third parameter (STD: mem_fun (& shape: Draw) passed to it.
(Shape: Draw is called by for_each, but you can actually wrap any function, as long as this function accepts one
Shape * parameters), for_each, the "behavior is different" occurs during the compilation period, so it is a static polymorphism.
As mentioned earlier,
Comparison between the template and interface inheritance,
The template is non-intrusive.
This is one of the essential differences between C ++ generics and object-oriented polymorphism mechanisms.
But in fact, object-oriented does not necessarily mean that interfaces must be used to achieve dynamic polymorphism. Some Dynamic scripting languages, such as Ruby,
Its polymorphism mechanism is not only Runtime (dynamic), but also non-intrusive (it does not need to be reused by inheriting from a specific interface ).
This is called duck typing [7]. If it is not because of efficiency problems, such a polymorphism mechanism is the most intuitive.
It is non-invasive and does not have to work in the compilation period. However, efficiency is at least visible in the future and in some fields.
A concern. Therefore, languages such as C ++ that distinguish between compiling and Runtime polymorphism still have their unique advantages.
In addition,
The type security advantage of generic programming also makes it move from C ++ to other mainstream static type languages,
Especially the C-family Java
And C #, have accepted generics in the past few years.
Special, Turing completeness, Meta Programming
C ++ templates support special features,
This gives it the possibility to implement the compile-time control structure,
And then brought about a complete Turing sub-language.
The special introduction of templates was originally intended only for efficiency purposes-different implementations are provided for different types. However, it was later discovered that
The IF/else and recursive control structures in the translation phase.
The template metaprogramming was initially proposed by Erwin Unruh at a conference in 1994. At that time, he wrote a program and typed it in a compilation error.
Print a sequence of prime numbers. In the history of C ++, this event was like Columbus discovering the New World. Use Bjarne stroustrup.
At that time, he and several others thought it was amazing. In fact, this indicates that the Turing completeness of the C ++ template system is
Later, Todd veldhuizen wrote a paper and built a metachinmap machine using the C ++ template. This was the first time that the system proved
The Turing completeness of the C ++ template. The next thing is just a matter of course-some ad hoc template meta programming skills are constantly being discovered,
For efficient construction,
Highly adaptive general components.
Eventually,
David Abrahams compiled one of the basic components in the boost Library:
Boost. MPL
Library. Boost. MPL uses the type and compile-time constants as the data, and template-specific as the means to implement STL during the compile-time. You can see
The common vector, you can see the transform algorithm, but the objects manipulated by the algorithm and the objects stored in the container are no longer in the runtime.
Is the type and constant of the compilation period. You can open a boost
Take a look at the sub-Library (such as boost. tuple or boost. variant.
However, after all, C ++ template metaprogramming is a sub-language that is discovered rather than invented. On the one hand, it is extremely
Useful. On the other hand, because it is not something that should be taken into consideration at the beginning of the language design, it is not just a syntax
First-class (clumsy); more importantly, the C ++ compiler does not know because it is not a first-class language feature.
The existence of C ++ metaprogramming. This means that for example, for the IF/else facility in the following compilation phase [8]:
Template <bool B, Class X, class Y>
Struct if _{
Typedef X type; // use X if B is true
};
Template <Class X, class Y>
Struct if _ <false, X, Y> {
Typedef Y type; // use y if B is false
};
Typedef if _ <(sizeof (foobar) <40), Foo, bar >:: type;
The compiler does not really select the IF/else branch, but performs the template's special matching step by step without knowing it. If you encounter
Templates such as boost. MPL have a very heavy library, which will seriously drag down the compilation speed. The Compiler performs a unique special match,
One template instance after another is instantiated To Get A typedef defined in it. After the template is finished, all the intermediate instances are instantiated.
All template instance types are discarded [9].
The most comprehensive and in-depth introduction to template metaprogramming is the c ++ template written by David Abrahams, author of the boost. MPL library.
Metaprogramming, the sample chapter (chapter 3) [10] provides an overview of template metaprogramming [11].
As for the template meta-programming, it should be noted that it is not a "mass technology ". In daily programming, Meta programming skills are rarely used. Other party
Surface, the C ++ template contains a lot of ad hoc skills. If one end is plunged, it is easy to see the trees do not see the forest, so you need to remind me
For beginners, even if they want to learn, they must keep their "height" and always remember the significance and purpose of metaprogramming so that they will not be lost.
The language details are endless.
C ++ 09 -- Evolution
Generic programming has achieved industrial success in C ++, thanks to its efficiency and versatility. However, after nearly ten years of use
Then, the shortcomings of the C ++ template as a mechanism for implementing C ++ generics are gradually exposed. For example, the most serious one for beginners
The problem is that when a template library is used, compilation errors that cannot be understood are often up to K bytes. These compilation errors are easy
Scare a beginner. The essential reason is why the compiler reports a compilation error that is hard to read because it is in the eyes of the compiler.
Only the type, but not the "type ". For example, an iterator is a type, which is also called a "concept" in C ++"
(Concept ). For example, STD: sort requires that the parameter must be a random access iterator. If you accidentally give it a non-random access
So the compiler does not complain "Hey! Instead of randomly accessing the iterator, you complain that you cannot find a certain overload operator (
Type, such as operator + (INT )). In the eyes of the compiler, there is no such concept as "iterator". This concept only exists in the programmer's mind.
And STL documents.
To help the compiler generate more friendly Information
(Of course,
There are many other important reasons [12]) C ++ 09
,
Adding the "type" to the first-class support brings many benefits to bring the generic programming in C ++ to a new height: More
Friendly, practical, and intuitive.
In addition,
The C ++ template metaprogramming is not supported by the first-class language yet,
On the one hand, because it is not as widely used as the general template programming,
This is not so urgent. On the other hand, the schedule of C ++ 09 cannot wait for a mature proposal. If the template metadata is used in the future
If it is more and more widely used, the first-class language support is inevitable.
Summary
This article provides an overview of C ++ templates and generic programming supported by C ++ templates. This section focuses on the causes of the birth of generic programming.
The process and meaning of programming, compared with other abstract means. In addition, we have introduced the template metaprogramming in C ++. Finally, the C ++ module is introduced.
The Board is enhanced in C ++ 09.
[1] B. stroustrup: A History of C ++: 1979-1991. Proc ACM history of programming ages
Conference (HOPL-2). March 1993.
[2] http://en.wikipedia.org/wiki/Alexander_Stepanov
[3] http://www.cs.rpi.edu /~ Musser
[4] In fact, the STL interval concept is proved to be an imperfect abstraction. Have you found that you want to pass a range to a function,
For example, STD: sort, you need to pass two parameters, one is the beginning of the interval, and the other is the end of the interval. This separated parameter transmission method is
It is unwise to prove it. In some cases, it may cause unnecessary troubles. For example, if you want to iterate a group of files, it indicates the range of these files.
Returned by a readdir_sequence function. to separate and express a range, you must write:
Readdir_sequence entries (".", readdir_sequence: files );
STD: for_each (entries. Begin (), entries. End (),: Remove );
If you only want to traverse this interval once, you may not want to declare the entries variable. After all, one name is too cumbersome.
Maybe you just want:
STD: for_each (readdir_sequence (".", readdir_sequence: files),: Remove );
The next generation of C ++ standards (C ++ 09) will solve this problem (define the interval as a whole ).
[5] Of course,
The language does not stipulate that the underlying implementation of template instantiation must instantiate a version for each combination of parameter types.
However
This solution is the most efficient. The abstract penalty is completely eliminated. On the other hand, the one size fit all solution is not unfeasible,
But there will always be indirect calls. This also demonstrates a typical advantage of a static type system: helping the compiler generate more efficient code.
[6] http://en.wikipedia.org/wiki/don't_repeat_yourself
[7] http://en.wikipedia.org/wiki/Duck_typing
[8] From Bjarne stroustrup's paper: Evolving a language in and for the Real World: C ++ 1991-2006
[9] As a result, the D language has added support for template metaprogramming directly, such as the static if-else statement that actually works during the compilation period.
Http://www.boost-consulting.com/mplbook/ [10]
[11] for the translation of chapter 3, see my blog: Deep Exploration metabases

Http://blog.csdn.net/pongba/archive/2004/09/01/90642.aspx

[12] Due to space reasons,
It is not possible to elaborate on other important significance of concept for C ++ generic programming,
If you are interested, refer to my
A blog: C ++ 0x series: concept! Concept!

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.