"STL Source Analysis" learning traits programming

Source: Internet
Author: User
Tags data structures traits

Houtie teacher in the "STL Source Analysis" said: Understand traits programming technology, just like to get "sesame open" mantra, from this to a glimpse of the mysteries of STL source. The importance of such a word is self-evident.
The iterator has been introduced before, knowing that different data structures have their own specific iterators, different iterators also have different characteristics, because the interface of the algorithm is unified, through the different attributes of the iterator, the algorithm automatically chooses the correct execution flow, and at the same time, it can improve the execution efficiency of the algorithm as far as possible. How does the algorithm know the properties of the iterator? This glorious task is to be accomplished traits. In the STL implementation, the traits programming technology obtains the massive application, it utilizes "the embedded type" the programming skill and the C + + template parameter derivation function, has made up the C + + type recognition insufficiency. By traits, the algorithm can extract the properties of the iterator to help the algorithm run correctly and efficiently.

One, why need traits programming technology

I said a lot about the glorious deeds of traits, but has not introduced traits what is really a thing, what is used to do. Traits in English interpretation is characteristic, the following will introduce the role of traits technology, step-by-step to uncover its mysterious veil.

1.1 Inline type declaration

1.1.1 Declare a local variable with the type of the object that the iterator refers to

The following is a function template that takes an iterator as a template parameter:

Template<typename iterator>
void func (iterator iter)
{
    //function Body
}

If now the algorithm needs to declare a variable, and the type of the variable is the iterator refers to the type of object, what should be done.

Template<typename iterator>
void func (iterator iter)
{
    *iterator var;//can you define a variable like this?
}

The above code is not compiled, although C + + support sizeof (), but does not support typeof (), even if it is used in the rtti nature of the typeid (), is only the name of the type, so can not be directly used to declare variables. You can use the parameter type inference mechanism of the function template to solve the problem at this time, for example:

Template<typename iterator, TypeName t>
void Func_impl (iterator iter, T t)
{
    T temp;//solves the problem here
    /here do the original work of Func ()
}

template<typename iterator>
void func (iterator iter)
{
    Func_impl ( ITER, *iter);//func's work was moved to Func_impl.

int main (int argc, const char *argv[])
{
    int i;
    Func (&i);
}

function Func as an external interface, the actual operation is performed by function Func_impl, and the problem is solved by derivation of the parameter type of function Func_impl, and obtaining the type T of iterator point to object.

1.1.2 The return type with the type of the object that the iterator refers to

It is now possible to solve the problem of declaring variables in function body by using the parameter type of function template, but the problem is again, what can be done if the return type is the type of the object that the iterator refers to.

Template<typename iterator>
(*iterator) func (iterator iter)
{
    //This defines the return type OK.
}

In this case, the parameter type derivation mechanism of the template is powerless, because it can only deduce parameters and cannot deduce the return type of the function. The STL solves this problem by embedding the type declaration, that is, adding an "attribute" inside the iterator, through which the algorithm can easily learn the type of object the iterator refers to, see the following code:

Template<typename t>
class iterator
{public
:
    typedef t value_type;//Inline type declaration
    iterator (T *p = 0): m_ptr (P) {}
    t& operator* () const {return *m_ptr;}
    //...

Private:
    T *m_ptr;
};

Template<typename iterator>
TypeName Iterator::value_type  //The type of object the iterator refers to as the return type, the length is a bit scary ...
func (iterator iter)
{return
    *iter;
}

int main (int argc, const char *argv[])
{
    iterator<int> iter (new int);
    Cout<<func (ITER) <<endl;  Output: Ten
}

The return type of the function func () must precede the keyword typename, and the reason is explained in the "C + + template learning" I wrote earlier, because T is a template parameter, the compiler knows nothing about T before compiling the instantiation func, that is, The compiler does not know Iterator<t>::value_type is a type, or a static member function, or a static data member, the function of the keyword TypeName is to tell the compiler that this is a type, so as to successfully compile.

1.2 native pointers are also an iterator

Before introducing the classification of iterators, it is said that the native pointer is also an iterator, at this point the problem comes, the native pointer is not a class type, it is not possible to define an inline type. Therefore, the above embedded type implementation does not completely solve the problem, that can be done for the native pointers to do specialized processing. The answer is yes, the use of template partial specificity can be done.

The concept of "generics thinking" defines a template as:

A special version that is designed for further conditional restrictions on template parameters.

This generic version allows T to
template<typename t>
class C
{ 
    //...
} for any type.  

It is easy to accept that the above class template has a biased version of the following form:

Template<typename t>
class c<t*> 
{
    //... 
};

This special version applies only to the case where T is the native pointer, and "T is the native pointer" is a further condition limit of "T for any type". Then how to use template-biased to solve the problem of the primitive pointer can not be embedded type. The iterator_traits described below is the key.

second, the iterator extractor--iterator_traits

2.1 Native pointers are not a class type

STL uses the ITERATOR_TRAITS structure to specifically "extract" the characteristics of the iterator, the value_type mentioned in the preceding code is one of the characteristics of the iterator:

Template<typename iterator>
struct iterator_traits
{
    typedef typename Iterator::value_type Value_ type;
};

If the iterator has a defined value_type, then after the iterator_traits effect, the value_type is Iterator::value_type, comparing the previous version and the Iterator_ Traits after the effect of the version:

Template<typename iterator>
TypeName Iterator::value_type  //This line is the return type
func (iterator iter)
{ return
    *iter;
}
Template<typename iterator> TypeName Iterator_traits<iterator> after the iterator_traits effect
;:: Value_type  //This line is returned type
func (iterator iter)
{return 
    *iter;
}

In terms of length, it seems that more code needs to be tapped, why bother adding a layer of indirection. Because the native pointer is also an iterator and is not a class type, the native pointer cannot define an inline type. Here, by implementing a iterator_traits version of the problem can be solved, the specific implementation is as follows:

The iterator_traits version of the
template<typename t> struct iterator_traits<t*> for the case where the iterator is a native pointer
{
    typedef T value_type;
};

When you do the function overload, you should have encountered the following situations:

function version one
void func (int *ptr)
{ 
    //...
}

Function version two
void func (const int *ptr)
{ 
    //...
}

The above two functions, although the number and position of functions, parameters and positions are the same, but they are not the same function, but a function overload of the case, that is, the function parameters of the const and non-const version is not the same, in the function version one, you can modify the pointer ptr point to the But not in function version two, because the incoming pointer ptr is a const pointer. It is possible to think of what happens when you pass a const pointer as a template parameter to the iterator_traits version of the previous statement.

Iterator_traits<const Int*>::value_type  //obtained value_type is a const int, not an int

When we want to use iterator_traits extract to remove value_type and declare a temporary variable, we find that the declared variable is a const type and cannot be assigned, which is contrary to our intention. We need a way to distinguish between const and non-const in order to avoid this misunderstanding, the answer is very simple, as long as another design of a iterator_traits biased version can be:

Template<typename t>
struct iterator_traits<const t*>
{
    typedef T value_type;
}

Now, whether it's a custom iterator, a native pointer int*, or a const int*, you can get the correct value_type through the iterator_traits.

types defined in the 2.2 iterator_traits

STL based on experience, defines the iterator most commonly used five kinds of types: value_type, difference_type, pointer, reference, Iterator_category, Any developer who wants to combine the containers they develop with the STL must define these five types for the iterator of the container that they develop, which can be iterator_traits by a unified interface to extract the corresponding type, listed below in the STL Iterator_ Full definition of traits:

Tempalte<typename i>
struct iterator_traits
{
    typedef typename I::iterator_category Iterator_ category;
    typedef typename I::value_type Value_type;
    typedef typeanme I:DIFFERENCE_TYPE Difference_type;
    typedef typename I::p ointer pointer;
    typedef typename I::reference Reference;

Here are a few of the five types:

(1) One of the iterator types: value_type
Value_type refers to the type of object that the iterator refers to, for example, the native pointer is also an iterator, and the native pointer int*,int is the type of object that the pointer refers to, known as Value_type.

(2) Two types of iterators: Difference_type
Difference_type is used to represent the distance between two iterators, for example:

int array[5] = {1, 2, 3, 4, 5};
int *ptr1 = array + 1;//point 2
int *ptr2 = array + 3;//point 4
ptrdiff_t distance = ptr2-ptr1;//result is Difference_type

In the code above, the type of the result of the pointer ptr2 and PTR1 is Difference_type, and for the native pointer, the STL takes the ptrdiff_t of C + + as the difference_type of the native pointer.

(3) Three types of iterators: Reference_type

Reference_type refers to the type of object the iterator refers to, Reference_type is generally used on the * operator overload of the iterator, and if Value_type is T, then the corresponding Reference_type is t&; if value _type is a const T, then the corresponding Reference_type is the const t&.

(4) Iterator type four: pointer

Pointer refers to the iterator refers to the object, that is, the corresponding pointers, for pointers, the most common function is operator* and operator-> two operators. Therefore, the iterator needs to work on the two operators in the appropriate overload:

t& operator* () const {return *ptr}//t& are reference type
t* operator-> () const {return PTR;}//T* I S pointer type

(5) Iterator type five: iterator_category

The role of iterator_category is to identify the movement characteristics of iterators and the operations that can be performed on iterators, from Iterator_category, the iterator can be divided into input iterator, Output iterator, Forward Iterator, bidirectional iterator, Random Access iterator five categories, specifically why this classification, in simple terms, is to maximize efficiency, this is one of the purposes of STL. The specific situation has been in my "STL Source analysis" Learning of the iterator, the detailed introduction, here is not to say more.

2.3 iterator_traits Complete definition

To ensure that the iterator_traits works, the STL provides a iterator class, and all custom iterators must inherit from it in order to guarantee that these custom iterators will be able to smooth hell other STL components collaborate, iterator classes are defined as follows:

Template<typename Category,
         TypeName T,
         typename Distance = ptrdiff_t, typename pointer
         = t*,
         TypeName Reference = t&>
struct iterator
{
    typedef Category iterator_category;
    typedef T VALUE_TYPE;
    typedef Distance DIFFERENCE_TYPE;
    typedef pointer pointer;
    typedef Reference Reference;
};

The class iterator does not contain any member variables, only the type definition, and therefore does not add additional burden. Since the following three types have default values, it is only necessary to provide the first two parameters when inheriting it, such as:

Template <typename t>
class Listiter:public Std::iterator<std::forward_iterator_tag, t>
{
    / /...
}


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.