结束迭代器递减的便携性如何?

how portable is end iterator decrement?(结束迭代器递减的便携性如何?)

本文介绍了结束迭代器递减的便携性如何?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

提前致谢.

解决方案

我认为这是相关条款:

<块引用>

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

表 68 列出了序列操作为某些类型提供顺序容器而不是其他容器.一个实现应提供这些所有容器类型的操作显示在容器"列中,以及应实施它们以采取摊销固定时间.

<前>+----------------------------------------------------------------------------+|表 68 |+--------------+-----------------+---------------------+--------------------+|表达 |返回类型 |运营 |集装箱||||语义||+--------------+-----------------+---------------------+--------------------+|a.front() |参考;|*a.begin() |矢量,列表,双端队列 |||const_reference |||||对于常数 a |||+--------------+-----------------+---------------------+--------------------+|a.back() |参考;|*--a.end() |矢量,列表,双端队列 |||const_reference |||||对于常数 a |||……………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………..........……………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………|a.pop_back() |无效 |a.erase(--a.end()) |矢量,列表,双端队列 |……………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………..........

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

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

//来自 VC++ 的 <list>执行参考回(){//返回可变序列的最后一个元素返回 (*(--end()));}const_reference back() const{//返回非可变序列的最后一个元素返回 (*(--end()));}

<小时>

表中代码注意事项:

<块引用>

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

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

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

<小时>

根据 Bo Persson 的说法,我手头的 C++ 标准的修订版 相对于表 68 存在缺陷.

<块引用>

提议的解决方案:

更改表68中的规格可选序列操作"中23.1.1/12 对于a.back()"来自

*--a.end()

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

和规范a.pop_back()"来自

a.erase(--a.end())

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

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

Just encountered decrement of end() iterator in my company source codes and it looks strange for me. As far as I remember this was working on some platforms, but not for the others. Maybe I'm wrong, however I couldn't find anything useful in standard about that. Standard only says that end() returns an iterator which is the past-the-end value, but is it guaranteed to be decrementable? How does code like that match the standard?

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

Thanks in advance.

解决方案

I think this is the relevant clause:

ISO/IEC 14882:2003 C++ Standard 23.1.1/12 – Sequences

Table 68 lists sequence operations that are provided for some types of sequential containers but not others. An implementation shall provide these operations for all container types shown in the "container" column, and shall implement them so as to take amortized constant time.

    +----------------------------------------------------------------------------+
    |                                  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 |
    ..............................................................................
    .              .                 .                     .                     .
    .              .                 .                     .                     .

So for the containers listed, not only should the iterator returned from end() be decrementable, the decremented iterator should also be dereferencable. (Unless the container is empty, of course. That invokes undefined behavior.)

In fact, vector, list and deque implementations that came with the Visual C++ compiler does it exactly like the table. Of course, that's not to imply that every compiler does it like this:

// 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()));
    }


Note about the code in the table:

ISO/IEC 14882:2003 C++ Standard 17.3.1.2/6 – Requirements

In some cases the semantic requirements are presented as C + + code. Such code is intended as a specification of equivalence of a construct to another construct, not necessarily as the way the construct must be implemented.

So while it's true that an implementation may not implement those expressions in terms of begin() and end(), the C++ standard specifies that the two expressions are equivalent. In other words, a.back() and *--a.end() are equivalent constructs according to the above clause. It seems to me that it means that you should be able to replace every instance of a.back() with *--a.end() and vice-versa and have the code still work.


According to Bo Persson, the revision of the C++ standard that I have on hand has a defect with respect to Table 68.

Proposed resolution:

Change the specification in table 68 "Optional Sequence Operations" in 23.1.1/12 for "a.back()" from

*--a.end()

to

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

and the specification for "a.pop_back()" from

a.erase(--a.end())

to

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

It appears that you can still decrement the iterator returned from end() and dereference the decremented iterator, as long as it's not a temporary.

这篇关于结束迭代器递减的便携性如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:结束迭代器递减的便携性如何?

基础教程推荐