《STL源码分析》学习笔记 — 迭代器概念
一、迭代器是一种智能指针
在学习数据结构时,我们实现过一个版本的迭代器 — 链表迭代器实现。从这个实现中,我们不难看出两件事:
1、迭代器本身是一个智能指针,是 RAII 思想的一种体现;
2、迭代器的设计需求源于算法的实现,算法需要一种遍历容器的统一的方式;但是迭代器的实现需要开发者对于容器有充分的了解。从这个角度来说,提供迭代器应该是容器设计者的工作。
二、迭代器相应型别
当我们需要在算法中使用迭代器时,很可能会用到其相关类型。比如说迭代器所指向之对象类型。那么我们如何得到一个迭代器所指向对象的类型呢?考虑使用函数模板推导:
template<class I, class T>
void func_impl(I iter, T t)
{
T tmp;
// ...
}
template<class I>
void func(I iter)
{
func_impl(iter, *iter);
}
迭代器相关类型不只是迭代器所指对象的类型一种而已。根据经验,最常用的类型由五种,然而并非任何情况下任何一种都可利用上述的模板参数推导机制来取得。不难发现,上述机制得以生效的根本原因在于迭代器重载了 operator* 使得我们可以得到其所指类型的实例。如果所有相关类型都用这种方法解决,那是要求迭代器提供多种方法以获取不同类型的实例。因此,我们需要更完善一致的解决方法。
1、 Incrementable traits
可自增特性:
template<typename> struct incrementable_traits {
}; // (1)
template<typename _Tp> requires is_object_v<_Tp>
struct incrementable_traits<_Tp*>
{
using difference_type = ptrdiff_t; }; // (2)
template<typename _Iter>
struct incrementable_traits<const _Iter>
: incrementable_traits<_Iter> {
}; // (3)
template<typename _Tp> requires requires {
typename _Tp::difference_type; }
struct incrementable_traits<_Tp>
{
using difference_type = typename _Tp::difference_type; }; // (4)
template<typename _Tp>
requires (!requires {
typename _Tp::difference_type; }
&& requires(const _Tp& __a, const _Tp& __b)
{
requires (!is_void_v<remove_pointer_t<_Tp>>); // PR c++/78173
{
__a - __b } -> integral;
})
struct incrementable_traits<_Tp>
{
using difference_type = make_signed_t<decltype(std::declval<_Tp>() - std::declval<_Tp>())>;
}; // (5)
这里是通过 requires 实现的模板特化。
(1)是非特化的模板,针对所有不满足特化情况;
(2)is_object_v 定义如下:
/// is_object
template<typename _Tp>
struct is_object
: public __not_<__or_<is_function<_Tp>, is_reference<_Tp>, is_void<_Tp>>>::type
{
};
template <typename _Tp>
inline constexpr bool is_object_v = is_object<_Tp>::value;
当模板参数解引用后的类型既不是函数也不是 void 也不是引用类型时,结果为真。此时该结构体内的 difference_type 为 ptrdiff_t。
(3)与(2)类似,是对 const 类型的特化。
(4)使用了 requires 类型要求,需要模板类型参数中含有 difference_type 属性。
(5)使用了复合 requires 约束,包括:
① 模板类型参数中不含 difference_type 属性;
② 模板参数不能为 void*;
③ 模板参数类型的两个实例相减之后得到的类型为整型;
满足这些条件的模板参数对应的 difference_type 为两个模板类型的实例相减之后的类型对应的有符号类型。
2、Readable traits
用于定义类型是否可读。
namespace __detail
{
template<typename> struct __cond_value_type {
};
template<typename _Tp> requires is_object_v<_Tp>
struct __cond_value_type<_Tp>
{
using value_type = remove_cv_t<_Tp>; };
} // namespace __detail
template<typename> struct indirectly_readable_traits {
}; // (1)
template<typename _Tp>
struct indirectly_readable_traits<_Tp*>
: __detail::__cond_value_type<_Tp>
{
}; // (2)