前向迭代器多通道保证的优势

Strength of the multi-pass guarantee for forward iterators

本文关键字:迭代器 多通道      更新时间:2023-10-16

考虑标准中前向迭代器的定义(草案n4659,[forward.iterators]/27.2.5):

类或指针类型X满足前向迭代器的要求,如果

  • X满足输入迭代器 (27.2.3) 的要求,
  • X满足DefaultConstructible要求(20.5.3.1),
  • 如果X是一个可变迭代器,reference是对T的引用;如果X是一个常量迭代器,reference是对const T的引用,
  • 表 97 中的表达式有效,并且具有指示的语义,并且
  • 类型X的对象提供多通道保证,如下所述。 [注释省略] 在以下情况下,aX类型的b两个可取消引用迭代器提供多通道保证:
    • a == b意味着++a == ++b
    • X是指针类型,或者表达式(void)++X(a), *a等效于表达式*a

[注意:a == b的要求意味着++a == ++b(对于输入和输出迭代器来说并非如此),并且通过可变迭代器(适用于输出迭代器)取消了对赋值数量的限制,允许使用带有前向迭代器的多通道单向算法。

[表97省略]

  • 如果ab相等,则ab都是可取消引用的,或者两者都不可取消引用。
  • 如果ab都是可取消引用的,则当且仅当*a*b绑定到同一对象时,a == b

多通道保证的目的似乎是允许以下代码:

*iter <---------------------------------------------------
X iter_copy(iter);                                       |
/* do something with iter_copy */                        |
++iter_copy; ... ++iter_copy;                            |
/* iter has not changed, and *iter now is equivalent     |
* to *iter before the operations on iter_copy */        |
*iter <---------------------------------------------------

然而,从形式上讲,多通道保证似乎只是意味着执行 iter 的副本并增加副本*iter保持不变,并且随后第二次递增到iter_copy可能会改变*iter

现在你的第一个想法可能是"嘟嘟,归纳!",但它似乎没有达到预期的结果;它所说的只是如果我们做一个iter_copy的副本并增加副本,那么*iter_copy是不变的,但它没有说明原始*iter

问题:是否可以证明指定的多通道保证意味着意图?

当然,有可能提出一种满足所有前向迭代器保证但不是完全多遍的类型。

class Evil {
int* p;
size_t idx;
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = int;
using pointer = int*;
using reference = int&;
Evil() : p(nullptr), idx(0) { }
Evil(int* p, size_t idx) : p(p), idx(idx) { }
Evil(Evil const& ) = default;
Evil& operator=(Evil const& ) = default;
~Evil() = default;
// only p participates in comparison
bool operator==(Evil const& rhs) const {
return p == rhs.p && idx % 2 == rhs.idx % 2; 
}
bool operator!=(Evil const& rhs) const { return !(*this == rhs); }
// incrementing is sort of destructive
Evil& operator++() {
++idx;
++p[idx % 2];
return *this;
}
Evil operator++(int) {
auto tmp = *this;
++*this;
return tmp;
}
int& operator*() { return p[idx % 2]; }
};

让我们来看看要求:

  • a == b意味着++a == ++b.检查。operator++()甚至不影响平等。
  • (void)*a, *a相当于*a。检查,取消引用不是破坏性的。
  • (void)++X(a), *a相当于*a。检查。递增一个会更改另一个int,而不是此迭代器当前"指向"的 int。所以这个条件也成立。
  • a == biff*a*b绑定到同一个对象。检查。

但是,(void)++++X(a), *a绝对不等同于*a。你会得到同样的int,它只会更大。

事实上,我可以想出一个不符合保证的滑稽不切实际的迭代器,这可能表明有一个实际的实用迭代器也不符合保证。那里有很多C++。