C ++对容器上迭代器行为的形式要求

时间:2015-11-21 13:32:25

标签: c++ language-lawyer

我确信我并不孤单,期望我可以按某种顺序向vectorlist添加多个元素,然后可以使用迭代器来检索这些元素的顺序相同。例如,在:

#include <vector>
#include <cassert>

int main(int argc, char **argv)
{
    using namespace std;
    vector<int> v;
    v.push_back(4);
    v.push_back(10);
    v.push_back(100);
    auto i = v.begin();
    assert(*i++ == 4);
    assert(*i++ == 10);
    assert(*i++ == 100);
    return 0;
}

...所有断言都应该通过,程序应该正常终止(假设在构造向量或向元素添加元素时没有抛出std::bad_alloc异常)。

但是,我无法将其与C ++标准中的任何要求进行协调(我正在考虑C ++ 11,但如果它们明显不同,也希望得到其他标准的答案)。< / p>

begin()的要求只是(23.2.1第6段):

  

begin()返回一个引用容器中第一个元素的迭代器。

我正在寻找的是要求或要求的组合,而这些要求又在逻辑上要求,如果i = v.begin(),那么++i将引用向量中的第二个元素(假设这样的元素存在) - 或者甚至是迭代器的连续递增将返回向量中的每个元素的要求。

修改

更一般的问题是,标准中的哪些(如果有的话)文本要求成功递增通过在序列(有序或无序)上调用begin()获得的迭代器实际访问序列的每个元素?

4 个答案:

答案 0 :(得分:3)

标准中并没有直截了当的说明

  

如果i = v.begin(),则++ i将引用第二个元素   矢量。

但是,对于向量迭代器,为什么可以从标准草案N4527 24.2.1 / p5中的以下措辞来暗示它。一般来说[iterator.requirements.general]:

  

进一步满足积分要求的迭代器   值n和可解除引用的迭代器值a(a + n)*(a + n)等效于*(addressof(*a) + n),称为连续   迭代器。

现在,std::vector的迭代器满足此要求,因此我们可以暗示++i等同于i + 1,因此意味着addressof(*i) + 1。由于其连续性,它确实是向量中的第二个元素。

编辑:

关于random在C ++ 11和C ++ 14标准中访问迭代器和连续存储容器的问题确实存在浑浊。因此,commity决定通过添加一组名为连续迭代器的额外迭代器来优化它们。您可以在相对提案N3884中找到更多信息。

答案 1 :(得分:1)

在我看来,我们需要将标准的两个独立部分放在一起,以便在此获得可靠的要求。我们可以从表101开始,这要求a[n]等同于*(a.begin() + n)的序列容器(具体来说,basic_stringarraydeqeue和{{1 (和vector的相同要求,对于相同的容器)。

然后我们查看[random.access.iterators]中的表111,它要求表达式a.at(n)等同于:

r += n

[缩进添加]

在这两者之间,这些暗示对于任何{ difference_type m = n; if (m >= 0) while (m--) ++r; else while (m++) --r; return r; } n指的是向量中的n th 项。如果你想要覆盖我看到的最后一个基础,我们将涵盖*(begin() + n)实际附加到该集合的要求。这也在表101中:push_back&#34;附加t&#34; (同样适用于a.push_back(t)basic_stringstringdequelist)。

答案 2 :(得分:0)

  

[C++14: 23.2.3/1]:序列容器将所有相同类型的有限对象组织成严格的线性排列。 [..]

我不知道你怎么解释它。

答案 3 :(得分:0)

规范不仅仅在迭代器中。它也在容器的规范中,以及修改这些容器的操作。

问题是,你不会找到一个单独的条款,说“反复递增begin()将按顺序访问向量的所有元素”。您需要查看每个容器上每个操作的规范(因为它们定义了容器中元素的顺序)和迭代器的规范(以及它们上的操作),这基本上是“递增移动到顺序中的下一个元素”对容器的操作定义,直到我们通过结束“。它是标准中众多条款的组合,可以产生最终效果。

然而,一般概念是....

所有容器都保持零个或多个元素的范围。该范围有三个关键属性:一个开头(对应于对容器有意义的顺序中的第一个元素),一个结束(对应于最后一个元素)和一个顺序(确定元素的顺序)一个接一个地检索 - 即定义“下一个”的含义)。

迭代器是一个对象,它引用一个范围中的元素,或者具有“超过结束”值。引用除结束(最后)之外的范围内的元素的迭代器,当递增时,将引用下一个元素。引用范围中的结束(最后)元素的迭代器,当递增时,将是结束(超过结束)迭代器。

begin()方法返回一个迭代器,它引用(或指向)范围中的第一个(如果范围为零元素,则返回结束迭代器)。 end()方法返回一个结束迭代器 - 一个对应于“一个超出范围结束”的迭代器。这意味着,如果使用begin()初始化迭代器,则重复递增它将在整个范围内按顺序移动,直到达到结束迭代器。

然后有必要查看容器的各种修饰符的规范 - 添加或删除元素的成员函数。例如,push_back()被指定为将元素添加到该容器的现有范围的末尾。它通过在末尾添加元素来扩展范围。

规范的组合 - 迭代器和修改容器的操作 - 保证了订单。实际效果是,如果元素按某种顺序添加到容器中,那么使用begin()初始化的迭代器将 - 当重复递增时 - 按照它们放置在容器中的顺序引用元素。

显然,一些容器修饰符稍微复杂一些 - 例如,std::vector的{​​{1}}被赋予一个迭代器,并在那里添加元素,随后移动元素以腾出空间。但是,关键点在于修饰符以定义的顺序将元素放入容器中(或者在像insert()这样的操作的情况下删除),迭代器将按照定义的顺序访问元素。