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

led*_*kol 52 c++ portability iterator stl

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

std::list<int>::iterator it = --l.end();
Run Code Online (Sandbox Code Playgroud)

提前致谢.

In *_*ico 51

我认为这是相关条款:

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()可递减的,而且递减的迭代器也应该是不可解的.(当然,除非容器是空的.这会调用未定义的行为.)

其实vector,listdeque实现,与Visual C++编译器就做它酷似表.当然,这并不是说每个编译器都这样做:

// 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()));
    }
Run Code Online (Sandbox Code Playgroud)

请注意表中的代码:

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

在某些情况下,语义要求表示为C + +代码.此类代码旨在作为构造与另一构造的等同性的规范,而不一定是构造必须实现的方式.

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


据柏Persson的,C++标准的修订,我手头上有一个缺陷相对于表68.

拟议决议:

更改23.1.1/12中表68"可选序列操作"中的"a.back()"中的规范

*--a.end()
Run Code Online (Sandbox Code Playgroud)

{ iterator tmp = a.end(); --tmp; return *tmp; }
Run Code Online (Sandbox Code Playgroud)

以及来自的"a.pop_back()"规范

a.erase(--a.end())
Run Code Online (Sandbox Code Playgroud)

{ iterator tmp = a.end(); --tmp; a.erase(tmp); }
Run Code Online (Sandbox Code Playgroud)

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

  • 我在这里看不到任何暗示我可以取消引用`end()`返回的迭代器的东西.你可能意味着可以解除引用递减的迭代器(否则,首先减少它的重点是什么)? (2认同)

Iza*_*ana 7

从文档中 std::prev

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

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

所以你应该更喜欢std::prev或执行以下操作: { auto end = a.end(); --end; };