迭代器概念与traits编程技法

我们用容器存放数据,用算法来执行操作,而迭代器就是两者的接合器。

首先来看find算法

inline const char *_Find(const char *_First, const char *_Last, int _Val)
    {    // find first char that matches _Val
    _First = (const char *)_CSTD memchr(_First, _Val, _Last - _First);
    return (_First == 0 ? _Last : _First);
    }

这是find的一种实现,其实它提供的接口是

template<class _InIt,
    class _Ty> inline
    _InIt find(_InIt _First, _InIt _Last, const _Ty& _Val)

把它简写一下就是:

template <class Iterator,class T>

inline find(Iterator first,Iterator last,const T&val)

给予不同的迭代器就可以在不同的容器中查询,通过*来访问对应的容器元素


2.traits技法

迭代器类似指针,常见的行为包括dereference和member access。即*和->运算符,因此所有的迭代器都必须重载operator*和operator->

这里主要有这么一个问题,对于迭代器类型,有时候需要返回其所指容器中包含的类型,但是在迭代器类型的定义中,由于容器内的这个类型是运行时确定的。

所以在定义时就无法获得该类型。

即template<class A>

class B{}


template<class B>void

function(B&){}

在function中对B的处理时,会用到B中的元素,但是这里因为不知道其类型,无法使用。

这个时候可以加一个新函数function1将B中的元素作为参数传入,同时有不同的模板参数

改成

template<class B,class A>void

function1(B&,A&){},其中A类型变量由B中提取,然后再在function中调用function1,就可以将原先计划在function中的处理移动到function1中。


但是,如果function想返回一个A类型的值要怎么办。这只能使用新的方法。

就是traits技法,其实这种技法就是在迭代器中获得其指向容器的保存元素的类型。

这个方法包括如下两步,1,内嵌型别声明,2,偏特化处理原生指针


1.内嵌型别声明

其实就是在容器类型中typedef声明其传入模板参数类型为一个新类型

template<class T>
struct Item
{
    typedef T value_type;
    T*ptr;
    Item(T *t):ptr(t){}
    ...
};

就类似这个Item结构中的value_type,就是内嵌类型声明。

然后到迭代器类中,我们就可以使用这个value_type。使用方式大致为


template<class I>
struct ItemIterator
{

     typename I::value_type function()
     {
         ...
     }

};

这里就可以使用这个类型,但是要注意,必须加上typename关键字。注意:这里不能使用class,我们都知道在模板定义中,class与typename基本相同,但是,

在作为嵌套依赖类型的时候,只能使用typename

如果你用class,会报错告诉你,value_type不是一个class。

例子如下:

#include <iostream>
template<class T=int>
struct Item
{
    typedef T value_type;
    T *ptr;
    Item(T *t):ptr(t){}

};

template<class I>
struct ItemIterator
{
    ItemIterator(I t):item(t){}
     typename I::value_type function()
     {
         return *(item.ptr);
     }
     I item;

};

int main()
{
    int b = 6;
    int *p = &b;
    Item<> c(p);
    ItemIterator<Item<> > iter(c);
    std::cout<<iter.function();
    return 0;
}

为什么使用内嵌类型,是为了获取迭代器保存的类型,这个类型是运行时确定的。

接着是偏特化,这里使用偏特化的主要原因是针对原生指针的情况

原生指针指的是类似int*之类的。为什么会有原生指针的问题。

我们来看下面这段代码:


#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
template<class T=int>
struct Item
{
    typedef T value_type;
    T *ptr;
    Item(T *t):ptr(t){}
    T&operator*()
    {
        return *ptr;
    }
};
void add1(int elem)
{
    elem+=1;
}

template<class I>
typename I::value_type func(I t)
{
    return *(t.ptr);
}

int main()
{
    int b = 6;
    int *p = &b;
    Item<> c(p);

    std::cout<<func(c);
    //std::cout<<func(p);
    int array[10]={0,1,2,3,4,5,6,7,8,9};
    for_each(array,array+9,add1);
    return 0;
}


我们准备实现一个算法func,它接收一个输入的迭代器类型,返回一个迭代器内保存的对象,而这个迭代器类型Item有一个内嵌类型声明value_type。这使得我们可以在算法func中返回这个value_type。

同时注意这里我们使用了算法for_each,它不仅仅接收迭代器类型,同时也接受原生指针,如int*。但是我们写的func如果传递int*,它是有问题的,因为int*没有value_type。


那么要如何保证我们写的算法func能接收原生指针呢。


为了解决这个问题,我们就使用一个class template来专门提取value_type即迭代器所指对象的型别,由这个template来架桥,对于非原生指针,它只是相当于一个隔层,对于原生指针乃至之后提到的const指针,它就相当于一个原生指针的接口,这个接口能提取符合要求的类型。

其实这个专门的class template等于一层封装,统一了接口

template<class I>
struct iterator_traits{
    typedef typename I::value_type value_type;
};

对于原生指针类型和I不同,它没有value_type,所以就需要使用偏特化

template<class T>

struct iterator_traits<T*>{

  typedef T value_type;

}

即将传入的模板参数作为value_type

这样即使int*不是一种类型,也可以获得其value_type


代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
template<class T=int>
struct Item
{
    typedef T value_type;
    T *ptr;
    Item(T *t):ptr(t){}
    T&operator*()
    {
        return *ptr;
    }
};

template<class I>
struct Iterator_traits
{
    typedef typename I::value_type value_type;
};
template<class I>
struct Iterator_traits<I*>
{
    typedef I value_type;
};
void add1(int elem)
{
    elem+=1;
}

template<class I>
typename Iterator_traits<I>::value_type func(I t)
{
    return *t;
};

int main()
{
    int b = 6;
    int *p = &b;
    Item<> c(p);

    std::cout<<func(c)<<endl;

    std::cout<<func(p)<<endl;
    int array[10]={0,1,2,3,4,5,6,7,8,9};
    for_each(array,array+9,add1);
    return 0;
}

通过Iterator_traits,我们传递的Item调用其*运算符,返回一个Iterator_traits<>::value_type(即Item::value_type类型也即是T类型)


这里还需要特化一次,因为我们会使用这个func的返回值,但是如果内部是const int*类型,那么返回值也是const int我们没办法将其当成一个左值,所以需要再提供一个特化版本。


那就是

template<class I>
struct Iterator_traits<const I*>
{
    typedef I value_type;
};

如果是const int*

那么这里的value_type会是const int,这样我们需要将其变成非const。因此这里内嵌的value_type就变成了非const


通过这两步,我们就创建两个模板类,一个是迭代器模板类,一个是萃取模板类,就可以提供一个统一的接口类型用于算法操作迭代器内置类型。


要求迭代器类型必须提供一个内嵌类型value_type用于萃取,一般常用的迭代器要提供5种内嵌类型。这部分留在下一章

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值