In a nutshell: Tags are for overloading, for optimization.

Take a simple advance as example, you might design:dom

template<class II, class D>
void advance(II& i, D n){
    while( n-- ) ++i;
}

However it have O(n) complexity, which is not acceptable when you have a random_access_iterator. So you may change your design like this:ide

template<class II, class D>
void advance_II(II& i, D n){
    while( n-- ) ++i;
}
template<class RAI, class D>
void advance_RAI(RAI& i, D n){
    i += n;
}
template<class II, class D>
void advance(II& i, D n){
    if(is_random_access_iterator(i)) // not yet designed
        advance_RAI(i, n);
    else
        advance_II(i, n);
}

However the version of function to use is decided at run-time, so we try to let compiler decided which method to choose at compile-time. So we give iterators tags. There are five tags:this

struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirection_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirection_iterator_tag {};

Now you can do this:spa

template<class II, class D>
void __advance(II& i, D n, input_iterator_tag){
    while( n-- ) ++i;
}
template<class RAI, class D>
void __advance(RAI& i, D n, random_access_iterator_tag){
    i += n;
}
template<class II, class D>
void advance(II& i, D n){
    __advance(i, n, iterator_traits<II>::iterator_category());
}

圖片描述

相關文章
相關標籤/搜索