前向迭代器的多遍保证强度
Strength of the multi-pass guarantee for forward iterators
考虑标准中前向迭代器的定义(草案 n4659,[forward.iterators]/27.2.5):
A class or pointer type X
satisfies the requirements of a forward iterator if
X
satisfies the requirements of an input iterator (27.2.3),
X
satisfies the DefaultConstructible
requirements (20.5.3.1),
- If
X
is a mutable iterator, reference
is a reference to T
; if X
is a constant iterator, reference
is a reference to const T
,
- The expressions in Table 97 are valid and have the indicated semantics, and
- Objects of type
X
offer the multi-pass guarantee, described below.
[note omitted]
Two dereferenceable iterators a
and b
of type X
offer the multi-pass guarantee if:
a == b
implies ++a == ++b
and
X
is a pointer type or the expression (void)++X(a), *a
is equivalent to the expression *a
.
[ Note: The requirement that a == b
implies ++a == ++b
(which is not true for input and output iterators) and the removal of the restrictions on the number of the assignments through a mutable iterator (which applies to output iterators) allows the use of multi-pass one-directional algorithms with forward iterators. - end note ]
[table 97 omitted]
- If
a
and b
are equal, then either a
and b
are both dereferenceable or else neither is dereferenceable.
- If
a
and b
are both dereferenceable, then a == b
if and only if *a
and *b
are bound to the same object.
多次通过保证的目的似乎是允许这样的代码:
*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
.
现在您的第一个想法可能是"duh, induction!",但似乎达不到预期的结果;它只是说,如果我们复制 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
。查看。递增 1 会更改 other int,而不是此迭代器当前 "points" 的那个。所以这个条件也成立。
a == b
当且仅当 *a
和 *b
绑定到同一个对象。查看。
但是,(void)++++X(a), *a
绝对不等同于*a
。您会得到相同的 int
,只是大了一个。
事实上,我可以想出一个不符合保证的可笑的不切实际的迭代器,这可能表明有一个实际实用的迭代器也不符合保证。那里有很多 C++。
考虑标准中前向迭代器的定义(草案 n4659,[forward.iterators]/27.2.5):
A class or pointer type
X
satisfies the requirements of a forward iterator if
X
satisfies the requirements of an input iterator (27.2.3),X
satisfies theDefaultConstructible
requirements (20.5.3.1),- If
X
is a mutable iterator,reference
is a reference toT
; ifX
is a constant iterator,reference
is a reference toconst T
,- The expressions in Table 97 are valid and have the indicated semantics, and
- Objects of type
X
offer the multi-pass guarantee, described below. [note omitted] Two dereferenceable iteratorsa
andb
of typeX
offer the multi-pass guarantee if:
a == b
implies++a == ++b
andX
is a pointer type or the expression(void)++X(a), *a
is equivalent to the expression*a
.[ Note: The requirement that
a == b
implies++a == ++b
(which is not true for input and output iterators) and the removal of the restrictions on the number of the assignments through a mutable iterator (which applies to output iterators) allows the use of multi-pass one-directional algorithms with forward iterators. - end note ][table 97 omitted]
- If
a
andb
are equal, then eithera
andb
are both dereferenceable or else neither is dereferenceable.- If
a
andb
are both dereferenceable, thena == b
if and only if*a
and*b
are bound to the same object.
多次通过保证的目的似乎是允许这样的代码:
*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
.
现在您的第一个想法可能是"duh, induction!",但似乎达不到预期的结果;它只是说,如果我们复制 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
。查看。递增 1 会更改 other int,而不是此迭代器当前 "points" 的那个。所以这个条件也成立。a == b
当且仅当*a
和*b
绑定到同一个对象。查看。
但是,(void)++++X(a), *a
绝对不等同于*a
。您会得到相同的 int
,只是大了一个。
事实上,我可以想出一个不符合保证的可笑的不切实际的迭代器,这可能表明有一个实际实用的迭代器也不符合保证。那里有很多 C++。