21Traits方法:可以用来萃取迭代器的特性
(接着上一篇文章说到原生指针与traits技巧的使用,这里拓展了一点其他类型的使用方法,和上一篇文章介绍的方法一样。)
Template
Struct iterator_traits //输入带有value_type的指针
{
Typedef typename I::value_type value_type;
}
Template
Typename Iterator_traits::value_type
Func(I ite)
{
Return *ite;
}
Template
Struct iterator_traits
{
Typedef T value_type;
}
为了让这个traits可以运作,每个迭代器都必须自己定义 内嵌型别 nest typedef!!!!
22如果希望容器能很好的与预定义的STL交互,那么就要为 自定义的容器 的迭代器 定义下面5个内嵌类型。
Template<class I> //容器对应的迭代器类型
Struct iterator_traits //非原生指针
{
Typedef typename I::iterator_category;
Typedef typename I::value_type; //获取迭代器所指的对象的类型
Typedef typename I::difference_type; //迭代器间距,容器的容量
Typedef typename I::pointer;
Typedef typename I::reference; //返回引用类型
};
Template
Typename iterator_traits::difference_type count(I first, I last, const T& value)
{
Typename iterator_traits::difference_type n=0;
For(;first!=last;++first)
If(*first ==value)
++n;
Return n;
}
//偏特化,得到“原生指针”的difference type
Template
Struct iterator_traits
{
Typedef ptrdiff_t difference_type;
}
23
迭代器可以分为以下几大类:
1 Input Iter
2 Output Iter
3 Forward Iter
4 Bidirectional Iter
5 Random Access Iter
我们一般会根据不同的iter采取不同的行为
Template
Void advance(InputIterator& I, Distance n)
{
If( is_random_access_iterator(i)) //判断迭代器的类型
Advance_RAI(I,n);
Else if(xxx)
Advance_xx(I,n);
//…..
}
这种做法只能在执行期才确定使用哪个版本(if - else ),会影响程序的效率,最好可以在编译时就选择正确的版本。
怎么做到在编译器就可以正确的获取迭代器的类型,并根据类型做出不同的响应? 答案是 traits技巧
我们可以通过一个模板的形式,并利用traits萃取出迭代器的种类,利用这个迭代器相应型别作为advance的第三个参数。
什么是traits?
Template
Struct iterator_traits
{
Typedef typename I::value_type value_type;
};
Trait的作用就是如果模板参数类I有自己的value type,那么通过traits,可以萃取出的value type就是I的value type,这样一来,我们在使用一个traits就可以得到这个模板参数I的value type
Template
Typename iterator_traits::value_type func(I ite)
{
Return ite; //操作会被重载
}
利用traits技法,可以在iterator上封装多一个iterator_trait,将类型信息作为其数据成员,这样一来就可以方便的获取一个模板迭代器的实际类型了
我们可以通过一个模板的形式,并利用traits萃取出迭代器的种类,利用这个迭代器相应型别作为advance的第三个参数。
首先定义5个类型
Struct input_iterator_tag{};
Struct output_iterator_tag{};
Struct forward_iterator_tag : public input_iterator_tag{};
Struct bidirectional_iterator_tag: public forward_iterator_tag{};
Struct random_access_iterator_tag : public bidirectional_iterator_tag{};
接下来就可以为advanced函数定义重载函数,每个函数都有一个相应上面类型的参数。
Template
Inline void advance(input_iterator &I, distance n, input_iterator_tag)
{
While (n--) ++i;
}
Template
Inline void advance(RandomAccessIterator& I, distance n,random_access_iterator_tag){};
上面是两个重载的函数,和以前不同的是加入了tag类型
接下来就是一个接口来在编译期判定执行那个函数
Template
Inline void advance(InputIterator& I, distance n)
{
Advance(I, n, iterator_traits
精华就在这里,通过traits的技法,将InputIterator的实际相关信息萃取出来,作为参数处理,这样可以很好的利用上重载的好处。而且 traits还支持偏特化性质。
}
24如果模板中有一个模板参数是基类,然后没有一个模板参数是子类的重载,那么使用子类传入时,会调用基类的模板
Template
Inline iterator_traits
Template
Inline iterator_traits
Template
Inline iterator_traits
{
Typedef typename iterator_traits
Return __distance(first, last, category() );
}
这个distance可以接收任何类型的迭代器,这里的inpputIterator是按照STL算法的命名规则,按所能接受的最初级别来命名。而如果这些迭代器有继承关系,那么在使用Out,For,Bid这3个迭代器的话会默认调用input那个类型的迭代器,这和一开始的【如果模板中有一个模板参数是基类,然后没有一个模板参数是子类的重载,那么使用子类传入时,会调用基类的模板】的说法一致
关于能提供traits的迭代器,STL提供了一个基类,让我们实现自己的迭代器。
25【24】
迭代器 需要设计适当的 associated type
容器 需要 设计适当的 迭代器
(因为只有容器才能知道如果利用迭代器来遍历自己,并执行迭代器所应该有的各种行为-如前进等)
算法 可以独立于 容器 和 迭代器 ,以迭代器为对外接口就可以
Traits技巧! 利用“内嵌类型和template参数推导(Typedef typename I::value_type value_type;)”使得C++可以执行编译期类型识别(因为C++本身不是一门强类型的语言),这是一项很强大的技术
。
26STL仅仅使用traits对迭代器加以规范,制定出iterator_traits(STL),而SGI则拓展了它,实现了__type_traits 用来萃取类型(type)(SGI)的特性,如果我们可以获得某个类型的特性,例如 是有由non-trivial default constructor等等,那么我们就可以在构造,析构等操作时,选择最有效率的措施,而采用直接操作提供效率。
struct __true_type{};
struct __false_type{};
template
struct __type_traits
{
typedef typename T::has_trivial_default_constructor;
};
template
struct Iterator
{
typedef __true_type has_trivial_default_constructor;
};
template
typename __type_traits::has_trivial_default_constructor hasTrivialDefaultConstructor()
{}
原文链接: https://www.cnblogs.com/aga-j/archive/2011/06/05/2073041.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/26787
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!