对于输入迭代器,为什么 a == b 并不意味着 ++a == ++b?
C++03 标准中的 §24.1.1/3 读取,
对于输入迭代器,a == b 并不意味着 ++a == ++b。(相等并不意味着 保证替代性质或 参考透明度。)算法 输入迭代器不应该 尝试通过相同的 迭代器两次。他们应该是单身 通过算法。值类型 T 不是 需要是可分配类型 (23.1)。可以使用这些算法 以 istreams 作为源 通过输入数据 istream_iterator 类。
我无法理解上面引文中的粗体文字。谁能帮助我理解这一点?
另外,下面的陈述(上面引用中的斜体文本)是什么意思?它与 a==b
和 ++a==++b
表达式有何关系?
平等并不 保证替代性质或 参考透明度。
§24.1.1/3 from C++03 Standard reads,
For input iterators, a == b does not imply ++a == ++b. (Equality does not
guarantee the substitution property or
referential transparency.) Algorithms
on input iterators should never
attempt to pass through the same
iterator twice. They should be single
pass algorithms. Value type T is not
required to be an Assignable type
(23.1). These algorithms can be used
with istreams as the source of the
input data through the
istream_iterator class.
I couldn't understand the bold text in the above quotation. Can anyone help me understanding this?
Also, what does the following statement (italicized text in the above quotation) mean? How is it related to a==b
and ++a==++b
expressions?
Equality does not
guarantee the substitution property or
referential transparency.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
对于输入迭代器,递增迭代器会使同一迭代器的副本无效。
所以:
所以当然不能保证
++a == ++b
。也就是说,a == b
并不意味着++a == ++b
。我认为“替换属性”意味着“对
a
的值执行的任何操作都与对b
的值执行相同的结果”,或类似的 - 有它可能指的是替代的各种版本,但都是沿着这些思路的。我认为在这种情况下,它一定意味着“稍后对b
做同样的事情”,因为如果a == b
并且我还没有这样做任何无效的东西,那么我使用a
和b
中的哪一个并不重要,它们引用流中的同一点。但是当我增加时,我必须选择一个并失去另一个,因此++a == ++b
很困难。“引用透明度”意味着不同的对象是独立的,即它们不是彼此的引用/指针或别名。与“替代属性”结合,这意味着:
相同序列中的输入迭代器通常引用相同的“实际数据”,例如文件句柄或其他内容,其本身包含可变状态。由于
a
和b
引用相同的文件句柄,并且它们的值取决于其状态,因此您没有引用透明度。这种缺乏就是替代失败的原因。前向迭代器通常也引用相同的底层数据(如容器),但只要您以只读方式使用它们(并且不以其他方式修改容器),它们就不会背叛这一点事实上,至少在您开始比较它们返回值的地址之前是这样。因此,它们自身的值具有有限的引用透明度,而输入迭代器则没有。它们本身仍然是引用,因此它们引用的内容仍然是别名。
For Input Iterators, incrementing an iterator invalidates copies of the same iterator.
So:
So certainly
++a == ++b
is not guaranteed. That is,a == b
does not imply++a == ++b
.I think the "substitution property" means "anything you do with the value of
a
has the same result as doing the same with the value ofb
", or similar - there are various versions of substitution that it might refer to, but something along those lines. I think in this context it must mean "later doing the same withb
", since ifa == b
and I haven't done anything invalid yet, then it doesn't matter which ofa
andb
I use, they refer to the same point in the stream. But when I increment, I do have to pick one and lose the other, hence the difficulty with++a == ++b
."Referential transparency" means that different objects are independent, i.e. they're not references/pointers to or aliases of each other. In combination with "substitution property" this means:
Input iterators in the same sequence typically refer to the same "actual data", like a file handle or whatever, which itself contains mutable state. Since
a
andb
refer to the same file handle, and their value is dependent on its state, you don't have referential transparency. This lack is why substitution fails.Forward iterators typically also refer to the same underlying data (like a container), but as long as you use them read-only (and don't otherwise modify the container), they don't betray this fact, at least not until you start comparing the addresses of the values they return. So they have a limited kind of referential transparency of their own value, that input iterators don't. They're still references themselves, so the things they refer to are still aliased.
正如解释所说:“它们应该是单通道算法。”
要点是输入流上的迭代器代表瞬态。一旦迭代器改变,该状态就不再存在;表示该状态的所有其他迭代器都将失效:一旦递增
a
,迭代器b
就会变得无效。As the explanation says: “They should be single pass algorithms.”
The point is that an iterator on an input stream represents a transient state. Once the iterator is changed, that state no longer exists; all other iterators representing that state are invalidated: once you increment
a
, the iteratorb
becomes invalid.提到的属性有:
替换属性
参考透明度
非正式地,这意味着没有值和对该值的引用之间的差异(因此创建了该术语)。
在命令式编程中,这是一个很难理解的概念,因为我们习惯于修改变量。 Rick Hickey(Clojure 背后)对身份和状态之间的区别进行了精彩的演讲,可能会对您有所帮助。它的要点是变量是一个身份。在任何时候,身份都指的是国家。状态永远不会改变,但是身份可以更改以引用另一个状态。
输入迭代器
如果我们在上面定义
F(x)
来表示++,那么这里的替换属性违规是“显而易见的” x
,那么我们知道,如果输入迭代器验证了替换属性,则以下内容将保持a == b =>; ++a == ++b
。然而,事实并非如此,因为递增输入迭代器可能会使来自同一源的所有其他输入迭代器无效。从 n3290 中的表 107(第 831 页,就在您引用的段落上方):
也就是说,当我们执行
++a
时,b
可能会变得无效,因此++b
本身将是未定义的行为。这直接违反了
++a == ++b
,因此替换属性不成立。引用透明度在这里更加明显。如果输入迭代器是引用透明的,则意味着它们与它们指向的值是不可区分的。显然情况并非如此,因为应用
++
不会增加值,而是增加迭代器。The properties referred to are:
Substitution Property
Referential Transparency
Informally, it means that there is no difference between a value and a reference to this value (thus how the term was coined).
In imperative programming it is a difficult concept to get, because we are used to modify our variables. Rick Hickey (behind Clojure) gives a nice talk about the distinction between Identity and State that may help you. The gist of it is that a variable is an Identity. At any point in time an Identity refers to a State. A State never changes, however an Identity may be altered to refer to another State.
Input Iterators
The substitution property violation is "obvious" here, if we define
F(x)
in the above to mean++x
, then we have that if input iterators verified the substitution property, the following would holda == b => ++a == ++b
.This is not true, however, because incrementing an input iterator may invalidate all other input iterators from the same source. From table 107 in n3290 (page 831, just above the paragraph you quoted):
That is, when we perform
++a
, thenb
may become invalid, and therefore++b
itself will be undefined behavior.This is a direct violation of
++a == ++b
, therefore the substitution property does not hold.The referential transparency is a bit more evident here. If Input Iterators were referentially transparent, it would mean that they would be indifferentiable from the value they point to. Clearly this is not the case, as applying
++
does not increment the value, but the iterator.输入迭代器定义只能读取一次的序列;输入
从键盘或管道将是一个很好的例子。增加一个
istream_iterator
实际上意味着在istream
中进一步阅读,提取字符,以便同一流上的其他
istream_iterator
对于他们的立场将不再有效。想象
字符流
"abcdefg..."
(字母表的总和),有两个istream_iterator
指向'a'
。增加其中之一它们将导致从流中读取
'b'
,而不是其他迭代器可以看到它。
Input iterators define a sequence which can only be read once; input
from a keyboard or a pipe would be a good example. Incrementing an
istream_iterator
effectively means reading further in theistream
,extracting characters, so other
istream_iterator
on the same streamwill no longer be valid with regards to their position. Imagine
a stream of characters
"abcdefg..."
(the alphabet, in sum), with twoistream_iterator<char>
pointing to the'a'
. Incrementing one ofthem will cause the
'b'
to be read from the stream, and no otheriterator can ever see it.
考虑输入迭代器可以连接到从键盘读取的流。递增迭代器意味着读取下一个字符。
另外,递增迭代器的副本并不意味着读取相同的字符。
Consider that an input iterator could be connected to a stream reading from the keyboard. Incrementing the iterator means reading the next character.
Also incrementing a copy of the iterator does not mean reading the same character.