Boost C++ 库

……世界上最受推崇且设计最专业的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码规范

新迭代器概念 (New Iterator Concepts) - Boost C++ 函数库

新迭代器概念 (New Iterator Concepts)

作者 David Abrahams, Jeremy Siek, Thomas Witt
联系方式 dave@boost-consulting.com, jsiek@osl.iu.edu, witt@styleadvisor.com
组织 Boost Consulting, Indiana University 开放系统实验室, Zephyr Associates, Inc.
日期 2006-09-11
编号这是 n1550=03-0133 的修订版,该版本已被 C++ 标准委员会库工作组采纳用于 Technical Report 1(TR1)。本提案是对论文 n1297n1477n1531 的修订。
版权 Copyright David Abrahams, Jeremy Siek, and Thomas Witt 2003.
摘要我们提议一套新的迭代器概念系统,将访问 (access) 和定位 (positioning) 独立对待。这使得这些概念能够更紧密地匹配算法的要求,并为实践中使用的迭代器提供更好的分类方式。

动机

标准的迭代器分类和要求存在缺陷,因为它们使用单一的概念层次结构来处理两个正交的问题:*迭代器遍历*和*数值访问*。结果是,许多以迭代器分类来表达要求的算法过于严格。此外,许多现实世界中的迭代器无法被准确分类。例如,具有随机访问遍历能力的基于代理(proxy-based)的迭代器,在法律上只能被归类为“输入迭代器”,因此泛型算法无法利用其随机访问能力。当前的迭代器概念层次结构倾向于迭代器遍历(因此有了这些分类名称),而涉及数值访问的要求则在多处混入。下表总结了当前迭代器分类中的数值访问要求。

现有迭代器分类中的数值访问要求
输出迭代器 *i = a
输入迭代器 *i可转换为T
前向迭代器 *iT&(或const T&一旦 issue 200 解决)
随机访问迭代器 i[n]可转换为T(同时i[n] = t一旦 issue 299 解决,对于可变迭代器是必须的)

由于迭代器遍历和数值访问在单一层次结构中混在一起,许多有用的迭代器无法得到适当的分类。例如:vector<bool>::iterator几乎是一个随机访问迭代器,但返回类型不是bool&(参见 issue 96 和 Herb Sutter 的论文 J16/99-0008 = WG21 N1185)。因此,vector<bool>的迭代器仅满足输入迭代器和输出迭代器的要求。这非常不直观,以至于 C++ 标准在这一点上自相矛盾。在 23.2.4/1 段落中指出,vector是一个支持随机访问迭代器的序列。

另一个难以分类的迭代器是变换迭代器(transform iterator),这是一种将一元函数对象应用于某些底层迭代器解引用后的值的适配器(参见 transform_iterator)。对于诸如times之类的一元函数,函数对象的返回类型operator*的返回类型显然需要是函数对象的result_type通常不是引用。因为随机访问迭代器被要求从operator*返回左值,如果你用变换迭代器包装int*,你得到的不是预期的随机访问迭代器,而是输入迭代器。

第三个例子可以在 Boost Graph Library 的顶点(vertex)和边(edge)迭代器中找到。这些迭代器返回顶点和边描述符,它们是即时创建的轻量级句柄。它们必须按值返回。结果是,它们当前的标注迭代器分类是input_iterator_tag,这意味着严格来说,您不能将这些迭代器与像min_element()。作为临时解决方案,引入了 Multi-Pass Input Iterator(多遍输入迭代器)概念来描述顶点和边描述符,但正如该概念的设计说明所建议的那样,需要更好的解决方案。

总之,许多有用的迭代器不适合当前的标准迭代器类别。其结果是出现了以下不良情况:

  • 迭代器经常被错误分类。
  • 算法要求比必要的更严格,因为它们无法将对随机访问或双向遍历的需求与对真实引用返回类型的需求分开。

对标准的影响

针对 TR1 的这一提案是一个纯粹的扩展。此外,新的迭代器概念与旧的迭代器要求向后兼容,且旧的迭代器与新的迭代器概念向前兼容。也就是说,满足旧要求的迭代器也满足新系统中相应的概念,而符合新概念模型的迭代器将自动满足相应的旧要求。

对工作草案可能(但未提议)的变更

本文中的扩展建议了我们可能对下一版标准工作草案做出的一些变更。这些变更不作为本 TR1 提案的正式部分。

对算法要求的变更

标准库中的算法可以从新的迭代器概念中受益,因为新概念提供了一种更准确的方式来表达它们的类型要求。结果是算法可以在更多情况下使用,且类型要求更少。

对于下一份工作草案(而非 TR1),委员会应考虑对算法的类型要求做出以下变更。这些变更以文本替换的形式表述,列出了每个文本替换适用的算法。

前向迭代器 (Forward Iterator) -> 前向遍历迭代器 (Forward Traversal Iterator) 和 可读迭代器 (Readable Iterator)

find_end, adjacent_find, search, search_n, rotate_copy, lower_bound, upper_bound, equal_range, binary_search, min_element, max_element

前向迭代器 (1) -> 单遍迭代器 (Single Pass Iterator) 和 可读迭代器, 前向迭代器 (2) -> 前向遍历迭代器 和 可读迭代器

find_first_of

前向迭代器 -> 可读迭代器 和 可写迭代器

iter_swap

前向迭代器 -> 单遍迭代器 和 可写迭代器

fill, generate

前向迭代器 -> 前向遍历迭代器 和 可交换迭代器 (Swappable Iterator)

rotate

前向迭代器 (1) -> 可交换迭代器 和 单遍迭代器, 前向迭代器 (2) -> 可交换迭代器 和 可增量迭代器 (Incrementable Iterator)

swap_ranges
前向迭代器 -> 前向遍历迭代器、可读迭代器 和 可写迭代器
remove, remove_if, unique

前向迭代器 -> 单遍迭代器、可读迭代器 和 可写迭代器

replace, replace_if
双向迭代器 (Bidirectional Iterator) -> 双向遍历迭代器 (Bidirectional Traversal Iterator) 和 可交换迭代器
reverse
双向迭代器 -> 双向遍历迭代器、可读迭代器 和 可交换迭代器
partition

双向迭代器 (1) -> 双向遍历迭代器 和 可读迭代器, 双向迭代器 (2) -> 双向遍历迭代器 和 可写迭代器

copy_backwards
双向迭代器 -> 双向遍历迭代器、可交换迭代器 和 可读迭代器
next_permutation, prev_permutation
双向迭代器 -> 双向遍历迭代器、可读迭代器 和 可写迭代器
stable_partition, inplace_merge
双向迭代器 -> 双向遍历迭代器 和 可读迭代器
reverse_copy
随机访问迭代器 (Random Access Iterator) -> 随机访问遍历迭代器 (Random Access Traversal Iterator)、可读迭代器 和 可写迭代器
random_shuffle, sort, stable_sort, partial_sort, nth_element, push_heap, pop_heap make_heap, sort_heap
输入迭代器 (2) -> 可增量迭代器 和 可读迭代器
equal, mismatch
输入迭代器 (2) -> 可增量迭代器 和 可读迭代器
转换

弃用项

对于下一份工作草案(而非 TR1),委员会应考虑弃用旧的迭代器标签 (tags) 以及 std::iterator_traits,因为它们将被单独的 traits 元函数取代。

vector<bool>

对于下一份工作草案(而非 TR1),委员会应考虑将vector<bool>::iterator重新分类为:随机访问遍历迭代器、可读迭代器和可写迭代器。

设计

迭代器要求将被分为两组。一组概念处理数值访问的语法和语义

  • 可读迭代器
  • 可写迭代器
  • 可交换迭代器
  • 左值迭代器

访问概念描述了与operator*andoperator->相关的要求,包括value_type, referencepointer等关联类型。

另一组概念处理遍历

  • 可增量迭代器
  • 单次遍历迭代器
  • 前向遍历迭代器
  • 双向遍历迭代器
  • 随机访问遍历迭代器

遍历概念的精化 (refinement) 关系如下图所示。

traversal.png

除了迭代器移动操作(例如operator++)之外,遍历概念还包括对位置比较(例如operator==andoperator<)的要求。将概念细分为“可增量 (Incrementable)”和“单遍 (Single Pass)”的原因是为了提供与原始输入和输出迭代器要求完全匹配的概念。

本提案还包括一个用于指定迭代器何时与另一个迭代器“可互操作”的概念,其含义是int*int const*.

  • 可互操作迭代器 (Interoperable Iterators)

新旧迭代器概念之间的关系见下图。

oldeqnew.png

与旧的迭代器要求类似,我们提供了用于基于遍历概念进行派发 (dispatching) 的标签。这些标签通过继承关联,如果与第一个标签关联的概念是第二个标签的精化,则该标签可以转换为另一个标签。

我们的设计重用了iterator_traits<Iter>::iterator_category来指示迭代器的遍历能力。为了指定任何旧式迭代器分类都无法涵盖的能力,迭代器设计者可以使用一个iterator_category类型,该类型既可以转换为最合适的派生程度最高的旧迭代器分类标签,也可以转换为相应的新迭代器遍历标签。

我们没有提供用于基于访问概念进行派发的标签,部分原因是由于我们无法找到一种方法来为旧式迭代器自动推断正确的访问标签。迭代器的可写性可能取决于其value_type的赋值性,而目前没有已知的方法可以检测任意类型是否可赋值。幸运的是,基于访问能力进行派发的需求不如基于遍历能力进行派发的需求那么迫切。

一个困难的设计决策涉及operator[]。指定operator[]直接的方法是让其返回类型为reference;与operator*相同。然而,朝这个方向发展意味着满足旧随机访问迭代器要求的迭代器不一定符合可读或可写左值迭代器的模型。相反,我们选择了一个符合 issue 299 首选解决方案的设计:operator[]仅被要求返回可转换为value_type的内容(对于可读迭代器),并被要求支持赋值i[n] = t(对于可写迭代器)。

提议文本

对 [lib.iterator.requirements] 的补充

迭代器数值访问概念 [lib.iterator.value.access]

在下表中,X是迭代器类型,a是类型为X, Rstd::iterator_traits<X>::reference, Tstd::iterator_traits<X>::value_typev是类型为T.

可读迭代器 (Readable Iterators) [lib.readable.iterators]

一个类或内置类型X模型 *Readable Iterator* 概念,其值类型为T如果除了X是可赋值和可复制构造的,并且以下表达式有效且符合所述语义。U是类型T.

可读迭代器要求(除了可赋值和可复制构造)
表达式 返回类型 注意/前提条件
iterator_traits<X>::value_type T 任何非引用、非 cv 限定的类型
*a 可转换为T
prea是可解引用的。如果a == b*a
等效于*b.
a->m U& prepre: (*a).m是明确定义的。等价于(*a).m.

可写迭代器 (Writable Iterators) [lib.writable.iterators]

一个类或内置类型X模型 *Writable Iterator* 概念,如果除了X是可复制构造的,并且以下表达式有效且符合所述语义。可写迭代器具有一个关联的*值类型集合*。

可写迭代器要求(除了可复制构造)
表达式 返回类型 前置条件
*a = o   pre: 的类型o在的值类型集合中X

可交换迭代器 (Swappable Iterators) [lib.swappable.iterators]

一个类或内置类型X模型 *Swappable Iterator* 概念,如果除了X是可复制构造的,并且以下表达式有效且符合所述语义。

可交换迭代器要求(除了可复制构造)
表达式 返回类型 后置条件
iter_swap(a, b) void 被指向的值被交换

[*注:* 符合 可读迭代器 (Readable Iterator)可写迭代器 (Writable Iterator) 概念模型的迭代器也符合 *可交换迭代器 (Swappable Iterator)*。*--注结束*]

左值迭代器 (Lvalue Iterators) [lib.lvalue.iterators]

*Lvalue Iterator* 概念增加了*a 解引用后返回的类型operator*必须是迭代器值类型的引用这一要求。

左值迭代器要求
表达式 返回类型 注意/断言
*a T& T是 *cv*iterator_traits<X>::value_type其中 *cv* 是可选的 cv 限定。prea是可解引用的。

如果X可写迭代器,那么a == b当且仅当*a*b。如果X是同一个对象。如果a == b蕴含*a*b.

迭代器遍历概念 [lib.iterator.traversal]

在下表中,X是迭代器类型,aandb是类型为X, rands是类型为X, Tstd::iterator_traits<X>::value_typev是类型为T.

可增量迭代器 (Incrementable Iterators) [lib.incrementable.iterators]

一个类或内置类型X模型 *Incrementable Iterator* 概念,如果除了X是可赋值和可复制构造的,并且以下表达式有效且符合所述语义。

可增量迭代器要求(除了可赋值、可复制构造)
表达式 返回类型 断言 (Assertion)
++r X& &r == &++r
r++    
*r++    
iterator_traversal<X>::type 可转换为incrementable_traversal_tag  

如果X可写迭代器,那么X a(r++);等效于X a(r); ++r;and*r++ = o等效于*r = o; ++r。如果X是同一个对象。如果T z(*r++);等效于T z(*r); ++r;.

单遍迭代器 (Single Pass Iterators) [lib.single.pass.iterators]

一个类或内置类型X模型 *Single Pass Iterator* 概念,如果以下表达式有效且符合所述语义。

单次遍历迭代器要求(除了可增量迭代器和相等可比)
表达式 返回类型 操作语义 断言/前置/后置条件
++r X&   prer是可解引用的;postr是可解引用的,或者r是 past-the-end
a == b 可转换为bool   ==在其域上是等价关系
a != b 可转换为bool !(a == b)  
iterator_traits<X>::difference_type 表示迭代器之间距离的有符号整数类型    
iterator_traversal<X>::type 可转换为single_pass_traversal_tag    

前向遍历迭代器 (Forward Traversal Iterators) [lib.forward.traversal.iterators]

一个类或内置类型X符合 *前向遍历迭代器* 概念的模型,如果除了满足X满足 Default Constructible 和 Single Pass Iterator 的要求外,以下表达式有效且符合所述语义。

前向遍历迭代器要求(除了默认构造和单次遍历迭代器)
表达式 返回类型 断言/注意
X u; X& 注意u可能有一个奇异值。
++r X& r == sandr是可解引用的,则蕴含++r == ++s.
iterator_traversal<X>::type 可转换为forward_traversal_tag  

双向遍历迭代器 (Bidirectional Traversal Iterators) [lib.bidirectional.traversal.iterators]

一个类或内置类型X符合 *双向遍历迭代器* 概念的模型,如果除了满足X满足 Forward Traversal Iterator 的要求外,以下表达式有效且符合所述语义。

双向遍历迭代器要求(除了前向遍历迭代器)
表达式 返回类型 操作语义 断言/前置/后置条件
--r X&  

pre: 存在s使得r == ++s。posts是可解引用的。

++(--r) == r. --r == --s蕴含r == s. &r == &--r.

r-- 可转换为const X&
{
  X tmp = r;
  --r;
  return tmp;
}
 
iterator_traversal<X>::type 可转换为bidirectional_traversal_tag    

随机访问遍历迭代器 (Random Access Traversal Iterators) [lib.random.access.traversal.iterators]

一个类或内置类型X符合 *随机访问遍历迭代器* 概念的模型,如果以下表达式有效并遵循所述语义。在下表中,距离iterator_traits<X>::difference_typeandn表示一个类型为距离.

随机访问遍历迭代器要求(除双向遍历迭代器外)
表达式 返回类型 操作语义 断言/前提条件
r += n X&
{
  Distance m = n;
  if (m >= 0)
    while (m--)
      ++r;
  else
    while (m++)
      --r;
  return r;
}
 
a + n, n + a X { X tmp = a; return tmp += n; }  
r -= n X& return r += -n  
a - n X { X tmp = a; return tmp -= n; }  
b - a 距离 a < b ?  distance(a,b) : -distance(b,a) pre: 存在一个值n距离使得a + n == b. b == a + (b - a).
a[n] 可转换为 T *(a + n) 前置条件:a 是 可读迭代器
a[n] = v 可转换为 T *(a + n) = v 前置条件:a 是 可写迭代器
a < b 可转换为bool b - a > 0 <是全序关系
a > b 可转换为bool b < a >是全序关系
a >= b 可转换为bool !(a < b)  
a <= b 可转换为bool !(a > b)  
iterator_traversal<X>::type 可转换为random_access_traversal_tag    

可互操作迭代器 (Interoperable Iterators) [lib.interoperable.iterators]

一个类或内置类型X符合单遍迭代器模型的类或内置类型Y如果以下表达式有效并遵循所述语义,则其与同样符合单遍迭代器模型的类或内置类型 *可互操作*。在下表中,x是一个对象,类型为X, y是一个对象,类型为Y, 距离iterator_traits<Y>::difference_typen表示一个类型为距离.

表达式 返回类型 断言/前置条件/后置条件
y = x Y posty == x
Y(x) Y postY(x) == x
x == y 可转换为bool ==在其定义域上是一个等价关系。
y == x 可转换为bool ==在其定义域上是一个等价关系。
x != y 可转换为bool bool(a==b) != bool(a!=b)在其定义域上。
y != x 可转换为bool bool(a==b) != bool(a!=b)在其定义域上。

如果XandY都符合随机访问遍历迭代器模型,则还必须满足以下附加要求。

表达式 返回类型 操作语义 断言/前提条件
x < y 可转换为bool y - x > 0 <是全序关系
y < x 可转换为bool x - y > 0 <是全序关系
x > y 可转换为bool y < x >是全序关系
y > x 可转换为bool x < y >是全序关系
x >= y 可转换为bool !(x < y)  
y >= x 可转换为bool !(y < x)  
x <= y 可转换为bool !(x > y)  
y <= x 可转换为bool !(y > x)  
y - x 距离 distance(Y(x),y) pre: 存在一个值n距离使得x + n == y. y == x + (y - x).
x - y 距离 distance(y,Y(x)) pre: 存在一个值n距离使得y + n == x. x == y + (x - y).

对 [lib.iterator.synopsis] 的补充

// lib.iterator.traits, traits and tags
template <class Iterator> struct is_readable_iterator;
template <class Iterator> struct iterator_traversal;

struct incrementable_traversal_tag { };
struct single_pass_traversal_tag : incrementable_traversal_tag { };
struct forward_traversal_tag : single_pass_traversal_tag { };
struct bidirectional_traversal_tag : forward_traversal_tag { };
struct random_access_traversal_tag : bidirectional_traversal_tag { };

对 [lib.iterator.traits] 的补充

is_readable_iterator类模板满足 UnaryTypeTrait 要求。

给定一个迭代器类型X, is_readable_iterator<X>::value将产生true如果对于对象a类型为X, *a可转换为iterator_traits<X>::value_typefalse否则。

iterator_traversal<X>::type

category-to-traversal(iterator_traits<X>::iterator_category)

其中 *category-to-traversal* 定义如下:

category-to-traversal(C) =
    if (C is convertible to incrementable_traversal_tag)
        return C;
    else if (C is convertible to random_access_iterator_tag)
        return random_access_traversal_tag;
    else if (C is convertible to bidirectional_iterator_tag)
        return bidirectional_traversal_tag;
    else if (C is convertible to forward_iterator_tag)
        return forward_traversal_tag;
    else if (C is convertible to input_iterator_tag)
        return single_pass_traversal_tag;
    else if (C is convertible to output_iterator_tag)
        return incrementable_traversal_tag;
    else
        the program is ill-formed

脚注

UnaryTypeTrait 概念在 n1519 中定义;LWG 正在考虑增加其特化版本应派生自其嵌套的::type.