迭代器分类标签及用途


不同编译器实现代码可能不完全一样,本文中代码均来自msvc.

迭代器分类标签 std::random_access_iterator_tag

随机存取标签,如下代码,这是一个空结构体,什么也不做,仅用来标识随机存取迭代器,除随机存取标识外还有其他类别的标识。

struct random_access_iterator_tag : bidirectional_iterator_tag {};

用途

这个标签有什么用呢? 不用可不可以?
迭代器分类标签用来进行编译期间的算法选择。这有两层意思:

  1. 这个标签用来做针对迭代器的算法选择
  2. 算法选择发生在编译阶段

常用迭代器分类标签

首先认识一下常用容器的迭代器的分类标签,

vector struct std::random_access_iterator_tag
list struct std::bidirectional_iterator_tag
deque struct std::random_access_iterator_tag
array struct std::random_access_iterator_tag
map struct std::bidirectional_iterator_tag
unordered_map struct std::bidirectional_iterator_tag
set struct std::bidirectional_iterator_tag
unordered_set struct std::bidirectional_iterator_tag

获取代码如下,

#include <iterator>
#include <vector>
#include <stdio.h>
#include <list>
#include <deque>
#include <array>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

using namespace std;

int main()
{
   iterator_traits<vector<int>::iterator>::iterator_category cati;
   iterator_traits<list<char>::iterator>::iterator_category catlc;
   iterator_traits<deque<int>::iterator>::iterator_category catqi;
   iterator_traits<array<int,5>::iterator>::iterator_category catai;
   iterator_traits<map<int, int>::iterator>::iterator_category catmii;
   iterator_traits<unordered_map<int,int>::iterator>::iterator_category catumii;
   iterator_traits<set<int, int>::iterator>::iterator_category catsii;
   iterator_traits<unordered_set<int, int>::iterator>::iterator_category catusii;

   printf("vector|%s\n", typeid (cati).name());
   printf("list|%s\n", typeid (catlc).name());
   printf("deque|%s\n", typeid (catqi).name());
   printf("array|%s\n", typeid (catai).name());
   printf("map|%s\n", typeid (catmii).name());
   printf("unordered_map|%s\n", typeid (catumii).name());
   printf("set|%s\n", typeid (catsii).name());
   printf("unordered_set|%s\n", typeid (catusii).name());
  
   return 0;
}

算法选择

然后我们看一下一个算法选择的例子,以std::distance为例子,distance用于获取从first到last的“行程”。

template< class InputIt >
typename std::iterator_traits<InputIt>::difference_type
    distance( InputIt first, InputIt last );

我们知道对于vector或者数组这样的随机存取容器,获取两个位置之间的行程非常简单,且时间复杂度为常数,因为元素是
连续存储的。而对于list和map等容器,行程的获取就略加复杂。因此我们希望针对不同容器可以有不同的求解算法,以下是代码实现:

template <class _InIt>
_NODISCARD _CONSTEXPR17 _Iter_diff_t<_InIt> distance(_InIt _First, _InIt _Last) {
    if constexpr (_Is_random_iter_v<_InIt>) {
        return _Last - _First; // assume the iterator will do debug checking
    } else {
        _Adl_verify_range(_First, _Last);
        auto _UFirst             = _Get_unwrapped(_First);
        const auto _ULast        = _Get_unwrapped(_Last);
        _Iter_diff_t<_InIt> _Off = 0;
        for (; _UFirst != _ULast; ++_UFirst) {
            ++_Off;
        }

        return _Off;
    }
}

从上边的代码可以看出,对于随机存取容器return _Last - _First;这一条表达式就可以了,而对于其他容器则需要一个一个的查找对比。

怎么将选择实现在编译阶段

编译阶段的算法选取通过if constexpr表达式实现,与普通的if else想比,后者的判断发生在程序执行阶段,而前者的判断发生在
编译阶段。

编译期间算法选择的另外一种方式

if constexpr语句是c++17之后出现的新特性,在此之前使用如下方式实现同样功能。

template <class _InIt>
_CONSTEXPR17 _Iter_diff_t<_InIt> _Distance1(_InIt _First, _InIt _Last, input_iterator_tag) {
    // return distance between iterators; input
    _Adl_verify_range(_First, _Last);
    auto _UFirst             = _Get_unwrapped(_First);
    const auto _ULast        = _Get_unwrapped(_Last);
    _Iter_diff_t<_InIt> _Off = 0;
    for (; _UFirst != _ULast; ++_UFirst) {
        ++_Off;
    }

    return _Off;
}

template <class _RanIt>
_CONSTEXPR17 _Iter_diff_t<_RanIt> _Distance1(_RanIt _First, _RanIt _Last, random_access_iterator_tag) {
    // return distance between iterators; random-access
    return _Last - _First;
}

template <class _InIt>
_NODISCARD _CONSTEXPR17 _Iter_diff_t<_InIt> distance(_InIt _First, _InIt _Last) {
    return _Distance1(_First, _Last, _Iter_cat_t<_InIt>());
}

另外可以看到,msvc中通过宏_HAS_IF_CONSTEXPR来分割了两种方法

// FUNCTION TEMPLATE distance
#if _HAS_IF_CONSTEXPR
#else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv

#endif // _HAS_IF_CONSTEXPR

猜你喜欢

转载自blog.csdn.net/iceboy314159/article/details/105904576