end()迭代器上的指针算术

时间:2014-09-19 07:25:31

标签: c++ stl iterator

设A为std::vector<double>

这个定义明确吗?

if(!A.empty())
    std::vector<double>::iterator myBack = A.end() - 1;

end迭代器是否仅适用于均等和不等式检查?或者只要我留在容器中,我就可以执行一些指针运算?

在我的平台上,此代码有效。我想知道这是否便携。

3 个答案:

答案 0 :(得分:5)

完全有效,因为vector::iterator是一个随机访问迭代器。您可以对它执行算术运算,它不依赖于平台。

std::vector<double>::iterator it = A.end();
while (it != A.begin()){
    --it; //this will skip A.end() and loop will break after processing A.front()
    //do something with 'it'
}

但是A.end()指的是理论上的过去元素,因此它不指向一个元素,因此不应被解除引用。因此,最佳实践是使用反向迭代器而不是递减结束迭代器。

for(std::vector<double>::reverse_iterator it = A.rbegin(); it != A.rend(); ++it) {
    //do something with 'it'
}

这两个循环做同样的事情,第二个是可以理解的,更清洁的方法。

答案 1 :(得分:4)

如果您注意到一些特殊情况,那么几乎安全:

A.end()为您提供一个迭代器,表示std::vector末尾的位置。您应该尝试取消引用它。

如果向量的元素为零,则A.end() - 1 定义良好。在所有其他情况下,只要您处于容器边界内,您就可以执行指针运算。请注意,该标准保证std::vector数据是连续的并且以与包含类型的C ++数组完全相同的方式打包。唯一的例外是std::vector<bool>,由于标准规定的紧密包装专业化,其行为有所不同。 (请注意,sizeof(bool) 保证标准具有特定值。

如果我是你,我会使用A.rbegin()来访问最右边的元素,并在继续之前检查返回值并坚持迭代器公式。忘记std::vector<bool>专业化很容易。

答案 2 :(得分:2)

我意识到这个问题有点老了,但我被引导到这里是关于 end() - 1,我发现现有的答案和评论内容丰富且合理,但由于缺乏引文而无法令人信服,而且我也不是确定它们是否特定于 vector。所以我尽可能多地挖掘具体信息,让自己相信这里的答案是正确的。

这篇文章代表了我为确认答案所做的研究,基本上是我的笔记,但我试图让它尽可能连贯,我认为它可能会有用。如果这里有任何问题,我们将非常感谢您的更正。


重述答案

这里的 TL;DR 是的,这里的答案是正确的,不仅适用于 vector,而且适用于更一般的情况:

如果容器的迭代器类型满足 BidirectionalIterator(因此提供递减操作),那么以下内容将始终有效,对于任何容器类型,其中 e 被初始化到 container.end() 的返回值:

  • 如果 !container.empty()--e 有效。
  • 如果 !container.empty()++(--e) == container.end() 为真。

如果迭代器也满足 RandomAccessIterator,那么这些更一般的语句是有效的:

  • e - ne -= n 用于 [ 0, container.size() ]< 中的任意整数 n /li>
  • e + ne += n 用于 [ - container.size() , 0 ] 中的任意整数 n

因此,OP 中的 vector 示例不仅很好,正如其他答案所述,而且定义明确,保证适用于任何容器类型。


推理

所以现在这里是我觉得缺少的一点。首先,从Container需求来看:

<块引用>
<头> 的最后一个元素之后
表达 返回类型 语义 条件 复杂性
a.end() (const_)迭代器 迭代器到 a 常数

这表示“超过最后一个元素”。但是,这是否意味着 end() 是可递减的?我们需要确定。下面的项目在这里很重要,我已经给它们编号以供参考:

  1. Container:“end() 返回一个超过上述a”要求的结尾。
  2. RandomAccessIteratori - n,根据 -= 定义,没有给出任何约束。
  3. RandomAccessIteratorr -= n,根据 += 定义,没有给出任何约束。
  4. RandomAccessIteratorr += n,根据 --rn < 0 定义,没有给出任何约束。
  5. BidirectionalIterator--a
    • 前提:a 是可递减的 → 存在 b 使得 a == ++b
    • 后置条件:a 是可取消引用的。
    • 后置条件:--(++a) == a
    • 后置条件:if --a == --b then a == b
    • 后置条件:a--a 是同一个迭代器实例。
  6. BidirectionalIterator注意:“双向迭代器不一定要可解引用才能可递减(特别是,结束迭代器不可解引用,但可递减)”。
  7. Container:声明 size() 在语义上等同于 std::distance(begin(), end())
  8. distance:返回从firstlast增量

分解:

(5) 的先决条件表明,要使 --a 起作用,a 必须是可递减的,然后继续定义迭代器 如果存在 b 使得 ++ b == a.

,a 是可递减的

(1) 的“最后一个”语言似乎暗示如果 b 是容器中最后一个元素的迭代器,则 {{1 }}。然而,更令人信服的是,(7)表明 ++ b == end() 必须有效,因此(8)意味着 std::distance(begin(), end()) 返回的迭代器必须能够重复递增,直到它等于 begin(),这意味着对于非空容器,在某个时刻必须存在一个 b 使得 end().

将这两者结合起来,表明 ++ b == end() 如果 end() 总是可递减的,因为总是有一个 b 使得 !empty()(否则 {{ 1}}——因此 ++ b == end()——将不满足其语义要求),这是可递减性的定义。另请注意,(6) 明确指出可递减迭代器不需要可取消引用,并说明了结束迭代器的可递减性。

此外,由于distance(begin(), end())size()时是可递减的,那么(其中end()被初始化为!empty()的返回值):

  • e 是有效的,来自 (5)。
  • container.end() 对于 -- e 是有效的,从 (4) 开始。
  • e += n 对于 n <= 0 是有效的,从 (3) 开始。
  • e -= n 对于 n >= 0 是有效的,来自 (2)。
  • 由于 e - nn >= 0+=(上面指出的 n 的符号)都是根据重复应用 {{1} },这将 n 限制在容器的大小范围内,因为 -= 不可递减(根据递减性的定义),最终迭代器必须命中 -

因此,只要在应用它的迭代器之前至少有 1 个元素,OP 中的 -- 就是有效的(来自 (2))。

Decrementability vs. dereferenceability:请注意,这是有区别的。 (6) 指出概念是分开的。可递减性意味着 begin() 是有效的,可解引用性意味着 begin()- 1 是有效的。在 OP 的 --i 示例中,虽然 *i 是可递减的,但它不可解引用(vector::end() 明确说明了这一点)。


代码

哦,是的,我也写了 a test program 只是为了检查:

i->

应该在不触发任何断言的情况下运行。


希望对你有帮助。几乎所有这些都是我努力说服自己vector确实有效