1. Why?
By default, a template provides a single definition that can be used for any template parameters that you can think! However, for the person who writes the template, this method is not flexible, especially when the template parameter is a pointer, if you want to instantiate a parameter different from the type, it becomes impossible! Sometimes, it is impossible to disable the same instantiation! So it appears, partial specialization!
At the same time, when using the void * pointer, you can share code to the maximum extent and reduce code expansion!
2. What is it? In fact, it is user-defined features.Template <> is used to describe that this is a special version. A special version is designed for parameters of any template type to make further condition restrictions!To put it bluntly, it isSome heavy-duty priority selection mechanisms, Select the most appropriate bitwise template for instantiation!
3. Significance of partial specialization: We provide a special version for the general design (that is, some template parameters in the general version are explicitly specified to influence the template instantiation type );
4. one of its applications in STL: traits programming techniques. The core point is to instantiate a template, whether it is a class instantiation template, a pointer, or a pointer to a constant, the types of template submission are all in line with program requirements !!! Therefore, traits is also called a "Feature Extraction Machine" by Mr. Hou Jie (you can hand over the correct element type for both types and pointers )!
5. without the special feature, there will be no widespread use of the so-called traits. Without traits, STL may not be a treasure of C ++, without STL Nb, c ++ may not be the language leader! It is precisely because of its small specialization that the iterator has made great achievements and separated the algorithm from the data structure!
1. The traits programming techniques are described in the previous article!
2. A good use is in the iterator;
3. Why? The premise is that not all template types are class type, some are native pointers, but some point to const T *
However, typedef t value_type cannot be used to extract the correct element type;
4. Whether it is a native pointer int * or const int *, you can use traits to retrieve the correct value_type;
5. It is the indirect layer of traits that separates algorithms from data structures! This makes the iterator a binder so that STL can work together!
6. Therefore,Iterator_traits must be designed with a special version when the input type is Pointer pointer and pointer to const!
Introduction
The extract programming technology is used in STL,
Brief description of extraction technology STL in iterator
STL (Standard Template Library) is a set builder of C ++ generic programming (template technology). iterator plays an important role in STL. There are three important concepts in STL:
- Containers, including sequential containers (
vector
,list
) And associated containers (map
,set
)
- Algorithm, function templates for various operations on containers (
count
,count_if
)
- The iterator serves as a bridge between algorithms and containers, allowing algorithms to develop independently of containers.
The important function of the iterator is to decouple the container and algorithm, or to decouple the data and operation. The algorithm uses the iterator as the input to get rid of the access to the specific container data. the container-generated iterator is used to traverse every element in the container and avoid exposing the internal data structure and implementation details of the container.
Here, an example of an algorithm is used to demonstrate the usage of the iterator:
template <class Iterator, class T>Iterator find(Iterator begin, Iterator end, const T& value){while (begin != end && *begin != value)++begin;return begin;}
Note the algorithm shown in this example.find()
Can be used in a variety of containers (vector
,map
...), If there is no iterator, You need to implementfind()
Algorithm.
Simple iterator
In the implementation of the iterator, you often need to access the type of the object referred to by the iterator.value type
. Use embedded type statementtypedef
You can easily hide the object type, as shown in the following iterator:
templates <class T>struct Iterator {typedef T value_type;...};
Generic algorithms can passtypename Iterator::value_type
To obtainvalue type
.
template <class Iterator>typename Iterator::value_typegetValue(Iterator iter) {return *iter;}
Note keywordstypename
BecauseT
Istemplate
The compiler does not know the parameter before it is instantiated.T
Is it a type or another object,typename
It is used to tell the compiler that this type can be compiled.
Concept of Extraction
In a simple iterator, the internal details of the object indicated by the embedded type declaration are well hidden and data and algorithms are separated. However, STL requires support for the iterator algorithms, native pointers should also be supported, such
Int array[4] = {1, 2, 3, 4};find(array, array+4, 3);
One problem exists here is that native pointers cannot be embedded with type declarations, so there is a need for multiple layers of encapsulation, and the extraction compilation technology came into being.
Traits programming technology is classified into four words: feature extraction. In the context of the iterator, the value type of the iterator is extracted. It can be conceptually considered that the type of the object referred to by the iterator is a feature of the iterator (traits)
template <class Iterator>struct iterator_traits {typedef typename Iterator::value_type value_type;...};
Withiterator_traits
To rewrite the algorithm.getValue()
:
template <class Iterator>typename iterator_traits<Iterator>::value_typegetValue(Iterator iter) {return *iter;}
The advantage of multi-layer encapsulation is that,iterator_traits
It is a C ++ class template, which can be customized for native pointers (Special iterators). <generic thinking> the template is customized as follows: for (any)template
The parameter is further restricted by a specially designed version, while the native pointerT*
,const T*
Is a kind of special.
template <class T>struct iterator_traits<T*> {typedef T value_type;};template <class T>struct iterator_traits<const T*> {typedef T value_type;};
Whether the iterator is a custom class template or a native pointer (T*
,const T*
),struct iterator_traits
Can extract the correct value type
STL iterator
Value Type is only a feature of the iterator (traits). In practice, the STL iterator defines a total of five features. In order to make the User-Defined iterator suitable for STL algorithms, STL makes a convention to write a base classstd::terator
As long as the custom iterator inheritsstd::iterator
,iterator_traits
We can extract all the features of the iterator correctly, so that the custom iterator can be integrated into the STL family and use a variety of generic algorithms seamlessly:
Template <class category, class value, class distance = ptrdiff_t, class pointer = value *, class reference = Value &> struct iterator {typedef category; typedef value value_type; typedef distance difference_type; typedef pointer; typedef Reference reference;}; the corresponding struct is defined as follows: Template <class iterator> struct {typedef typename iterator: value_typevalue_type; typedef typename iterator: difference_type struct; typedef typename iterator: pointer; typedef typename iterator: Reference reference; typedef typename iterator: iterator_category ;}; template <class T> struct iterator_traits <t *> {typedef t value_type; typedef ptrdiff_t difference_type; typedef T * pointer; typedef T & reference; typedef extends iterator_category ;}; template <class T> struct <const T *> {typedef t value_type; typedef ptrdiff_t difference_type; typedef const T * pointer; typedef const T & reference; typedef incluiterator_category ;};
The above iterators involve four other features, which are limited by space and will not be described. Interested readers will read the references below.
Type Extraction
The extraction (traits) programming technology makes upC++
HoweverSTL
It only regulates the characteristics of the iterator and developsiterator_traits
. Since this technology is so useful, it should be applied to more extensive application scenarios. sgi stl (STL version developed by the Silicon Graphics System) has made an attempt to apply it outside the world of the iterator, therefore, the concept of Type extraction is generated.
We know that,C++
Custom types have many features, such as constructor, copy constructor, and destructor. On the other hand,C++
Built-in type Integerint
,long
No constructor, copy constructor, and destructor. based on these features, we can use the most effective measures to construct and assign values. For example, for built-in types, we do not need to call constructor or copy constructor, but directly use memory processing (malloc()
,memcpy()
) To achieve the highest efficiency, which significantly improves the performance of large-scale and frequently-operated containers.
Simple Example
To use these features in the type, you can define a Feature Extraction Machine for the type:__type_traits
struct __true_type {};struct __false_type {};Template <class T>struct __type_traits {typedef __false_type has_trivial_default_constructor;typedef __false_type has_trivial_destructor;};
Because embedded type declarations cannot represent true or false, we define struct here__true_type
And__false_type
. By default, these features are based on the most conservative values. Then, based on the specific situation, the template is used to set a more optimistic value for the specific type. for example, the built-in int type definition template is special:
template <>struct __type_traits<int> {typedef __true_type has_trivial_default_constructor;typedef __true_type has_trivial_destructor;};
According to <STL source code analysis> introduction, Some compilers can analyze various types of programs and generate corresponding__type_trait
Template specialization. Of course, for compilers that do not support this function, manually compile the template specialization to define a more optimistic value. Otherwise, the default value__false_type
Will take effect.
Sgi stl Extraction
Defined by SGI STL__type_traits
In addition to the featureshas_trivial_default_constructor
Andhas_trivial_destructor
And other features, as described below:
<PRE name = "code" class = "CPP"> template <class type> struct _ type_traits {typedef _ true_type this_dummy_member_must_be_first; // customize typedef _ false_type handler for special compilers; typedef _ false_type has_trivial_copy_constructor; typedef _ false_type handler; typedef _ false_type has_trivial_destructor ;};
Application Scenario-copy
Template Functionscopy()
YesSTL
A generic algorithm has many special templates to Improve the Performance in different scenarios. We hope to use the method of Raytheon in appropriate cases to achieve the highest performance, for example, for different types, depending on whether there is a copy constructor, it can have different operations:
// Copy an array whose elements are of any type. Use the most effective Copying Method template <class T> inline void copy (T * Source, T * destination, int N) as appropriate) {copy (source destination, N, typename _ type_traits <t >:: has_trivial_copy_constructor ();} // copy an array, its element type does not have trival copy constructorstemplate <class T> void copy (T * Source, T * destination, int N, _ false_type ){...} // copy an array with the element type trival copy constructorstemplate <class T> void copy (T * Source, T * destination, int N, _ true_type ){...}
Application Scenario-destructor in the memory pool
In the implementation of the memory pool, the acquisition and release of the object's memory space are required. In general, to release the dynamic memory space of an object, you need to call the destructor of the object, therefore, you can define the Destructor template.destruct()
To execute the destructor. On the other hand, if the object does not require the destructordestuct()
Do nothing.
Template <class T> void destruct (T & T) {typedef typename type_traits <t>: has_trivial_destrutor operator ;__ destruct (T, has_trivial_destructor ());} template <class t >__ destruct (T & T, _ ture_type) {// destructor t} template <class t >__ destruct <T & T, _ false_type) {// nothing else}
Applying extract technology to convert Enumeration type to real type
In actual code writing, we often encounter this situation: an enumeration constant is known to obtain the corresponding real type, which can be solved by extraction technology, such as defining an enumeration constant:
Typedef Enum fieldtype {struct = 0, struct, struct, fieldtype_int16, struct, fieldtype_int32, fieldtype_uint32, fieldtype_int64, struct, struct, fieldtype_double, fieldtype_cstring,} efieldtype; the extraction technology can be used to define the Extraction Machine variabletypetraits to obtain the actual type corresponding to the enumeration type: Template <fieldtype Pt> struct variabletypetraits {typedef void syntaxtype ;};
The preceding meaning indicates that ifpt
Has a featurevoid
, You can useVariableTypeTraits<pt>::SyntaxType
void
Extracted. Of course, the above module is just an empty shell, because for different enumeration typesVariableTypeTraits<pt>::SyntaxType
Extractvoid
Is meaningless. In this case, the template features come in handy. Through the template features, you can define the corresponding types for each Enumeration type:
# Define struct (PT, type) template <> struct variabletypetraits <PT >\{ typedef type syntaxtype ;}; struct (fieldtype_int8, int8_t) struct (struct, uint8_t) struct (fieldtype_int16, int16_t) values (cost, uint16_t) values (fieldtype_int32, int32_t) values (fieldtype_uint32, uint32_t) values (cost, int64_t) values (cost, float) variable_type_traits_helper (fieldtype_double, double) the macro-defined implementation method is used to avoid code duplication. after defining the preceding content, you can easily obtain the actual type pointed to by the enumeration constant: void fun (fieldtype ft) {Switch (FT) {Case fieldtype_int32: typedef variabletypetraits <fieldtype_int32> :: syntaxtype; syntaxtype Foo ;...}
}
Sample Code
First, I will organize the code snippets in the previous article into a complete program to deepen the reader's understanding of this usage.
// Traits_example1.cpp # include <stdio. h> # include <stdlib. h> # include <stdint. h> // define typedef Enum fieldtype {fieldtype_undefined = 0, fieldtype_int8, fieldtype_int32 for Enumeration type definitions. // to simplify the problem, only two types are listed.} efieldtype; // use the traits programming technique template <fieldtype Pt> struct variabletypetraits {typedef void syntaxtype ;}; template <> struct variabletypetraits <fieldtype_int8 >{ typedef int8_t syntaxtype ;}; template <> struct variabletypetraits <fieldtype_int32> {typedef int32_t syntaxtype ;}; // test the demo function void func (int8_t var) {fprintf (stdout, "type: int8_t; value: % d \ n ", VAR);} void func (int32_t var) {fprintf (stdout," type: int32_t; Value: % d \ n ", VAR );} int main () {typedef struct <strong >:: syntaxtype int8_type; int8_type int8_a = 1; func (int8_a); typedef struct <strong >:syntaxtype int32_type; int32_type int32_ B = 2; func (int32_ B); Return 0 ;}
Convert a real type to an enumeration type
Similarly, the extraction technology can be used to convert the real type to the enumeration type:
template <typename T>struct TypeTraits {static const FieldType FIELD_TYPE = FIELDTYPE_UNDEFINED;}
Note that the nested type statement cannot be used here (typedef
), Because the enumerated variables are objects rather than types, the static constants of the class are used to define them.FIELD_TYPE
Next, you can define template-specific for each type without the template-specific type.FILED_TYPE
=FIELDTYPE_UNDEFINED
#define TYPE_TO_FIELDTYPE(type, field_type) template<> struct TypeTraits<type> { static const FieldType FIELD_TYPE = field_type; };TYPE_TO_FIELDTYPE(int8_t, FIELDTYPE_INT8);TYPE_TO_FIELDTYPE(uint8_t, FIELDTYPE_UINT8);TYPE_TO_FIELDTYPE(int16_t, FIELDTYPE_INT16);TYPE_TO_FIELDTYPE(uint16_t, FIELDTYPE_UINT16);TYPE_TO_FIELDTYPE(int32_t, FIELDTYPE_INT32);TYPE_TO_FIELDTYPE(uint32_t, FIELDTYPE_UINT32);TYPE_TO_FIELDTYPE(int64_t, FIELDTYPE_INT64);TYPE_TO_FIELDTYPE(uint64_t, FIELDTYPE_UINT64);TYPE_TO_FIELDTYPE(float, FIELDTYPE_FLOAT);TYPE_TO_FIELDTYPE(double, FIELDTYPE_DOUBLE);
It is also relatively simple to use.TypeTraits<int>::FIELD_TYPE
You can getint
Enumeration type corresponding to the typeFIELDTYPE_INT32
Summary
To sum up, there are two main advantages of traits programming skills.
- You can upgrade the judgment by using "Features" to the compilation level, rather than the runtime. This improves the performance.
- Universal implementation and data decoupling can be achieved
STL source code analysis-Implementation of extraction programming (traits) Technology