可移动性如何减少结束迭代器?

时间:2011-03-16 07:15:53

标签: c++ stl iterator portability

刚刚在公司的源代码中遇到了end()迭代器的减量,对我来说这看起来很奇怪。据我所知,这是在一些平台上工作,但不适用于其他平台。也许我错了,但是在标准方面我找不到任何有用的东西。标准只说end()返回一个迭代器,它是过去的结束值,但它是否保证可递减?这样的代码如何与标准相匹配?

std::list<int>::iterator it = --l.end();

提前致谢。

3 个答案:

答案 0 :(得分:50)

我认为这是相关条款:

  

ISO / IEC 14882:2003 C ++标准23.1.1 / 12 - 序列

     

表68列出了序列操作   为某些类型的提供   顺序容器而不是其他容器。   实施应提供这些   所有容器类型的操作   显示在“容器”栏中,和   应实施它们以便采取   摊销不变的时间。

    +----------------------------------------------------------------------------+
    |                                  Table 68                                  |
    +--------------+-----------------+---------------------+---------------------+
    |  expression  |   return type   |     operational     |      container      |
    |              |                 |      semantics      |                     |
    +--------------+-----------------+---------------------+---------------------+
    | a.front()    | reference;      | *a.begin()          | vector, list, deque |
    |              | const_reference |                     |                     |
    |              | for constant a  |                     |                     |
    +--------------+-----------------+---------------------+---------------------+
    | a.back()     | reference;      | *--a.end()          | vector, list, deque |
    |              | const_reference |                     |                     |
    |              | for constant a  |                     |                     |
    ..............................................................................
    .              .                 .                     .                     .
    .              .                 .                     .                     .
    ..............................................................................
    | a.pop_back() | void            | a.erase(--a.end())  | vector, list, deque |
    ..............................................................................
    .              .                 .                     .                     .
    .              .                 .                     .                     .

因此,对于列出的容器,不仅应该从end()返回的迭代器是可递减的,递减的迭代器也应该是可解除引用的。 (当然,除非容器是空的。这会调用未定义的行为。)

事实上,Visual C ++编译器附带的vectorlistdeque实现与表格完全相同。当然,这并不意味着每个编译器都这样做:

// From VC++'s <list> implementation

reference back()
    {    // return last element of mutable sequence
    return (*(--end()));
    }

const_reference back() const
    {    // return last element of nonmutable sequence
    return (*(--end()));
    }

注意表中的代码:

  

ISO / IEC 14882:2003 C ++标准17.3.1.2/6 - 要求

     

在某些情况下是语义   要求以C ++表示   码。 此类代码旨在作为   等价规范   构建到另一个构造,而不是   必然是构造的方式   必须实施。

因此,虽然实现可能无法在begin()end()方面实现这些表达式,但C ++标准指定这两个表达式是等效的。换句话说,a.back()*--a.end()是根据上述条款的等效结构。在我看来,这意味着您应该能够用a.back()替换*--a.end()的每个实例,反之亦然,并且代码仍然有用。


根据Bo Persson的说法,我手边的{+ 3}}修订了C ++标准。

  

提议的决议:

     

更改表68中的规格   “可选序列操作”   23.1.1 / 12来自

的“a.back()”
*--a.end()
     

{ iterator tmp = a.end(); --tmp; return *tmp; }
     

和规范   来自

的“a.pop_back()”
a.erase(--a.end())
     

{ iterator tmp = a.end(); --tmp; a.erase(tmp); }

看起来您仍然可以减少从end()返回的迭代器,并取消引用递减的迭代器,只要它不是临时的。

答案 1 :(得分:2)

来自std::prev的文档

  

尽管表达式--c.end()通常会编译,但不能保证这样做:c.end()是一个右值表达式,并且没有迭代器要求指定保证右值递减工作。特别是,当迭代器实现为指针时,--c.end()不会编译,而std :: prev(c.end())会编译。

这意味着前缀递减操作的实现可能不是inside class形式的iterator iterator::operator--(int),而是重载了类iterator operator--(iterator&, int)之外的类。

,因此您应该选择std::prev或执行以下操作: { auto end = a.end(); --end; };

答案 2 :(得分:0)

此代码不正常,如果列表为空,则表示您遇到问题。

因此检查一下,如果列表不为空,则代码非常精细。