端迭代器衰减的可移植性如何?

时间:2022-07-27 18:19:25

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?

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

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

Thanks in advance.

提前谢谢。

2 个解决方案

#1


46  

I think this is the relevant clause:

我认为这是相关条款:

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

ISO/ iec14882:2003 c++标准23.1.1/12 -序列

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.

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

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

因此,对于列出的容器,不仅从end()返回的迭代器应该是decrementable,而且decrementiterator也应该是可撤销引用的。当然,除非容器是空的。调用未定义行为)。

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:

实际上,带有Visual c++编译器的vector、list和deque实现与表完全一样。当然,这并不意味着每个编译器都这样做:

// 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

ISO/ iec14882:2003 c++标准17.3.1.2/6 -要求

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.

在某些情况下,语义需求被表示为c++代码。这些代码的目的是作为构造到另一个构造的等价性的规范,而不一定是构造必须实现的方式。

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.

因此,虽然实现可能不会按照begin()和end()实现这些表达式,但是c++标准指定这两个表达式是等价的。换句话说,a.back()和*- a.end()是根据上述子句的等价构造。在我看来,这意味着您应该能够用*- a.end()替换每个a.o back()实例,反之亦然。


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

根据Bo Persson的说法,我手头的c++标准的修订有一个关于表68的缺陷。

Proposed resolution:

建议解决方法:

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

将表68中的“可选序列操作”中的规格从23.1.1/12更改为“a.back()”

*--a.end()

to

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

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

以及“a.pop_back()”的规范

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.

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

#2


0  

This code is not OK, in case list is empty you are in trouble.

这段代码不行,如果列表为空,你就有麻烦了。

So check for that, if list is not empty the code is very fine.

检查一下,如果列表不是空的代码很好。

#1


46  

I think this is the relevant clause:

我认为这是相关条款:

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

ISO/ iec14882:2003 c++标准23.1.1/12 -序列

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.

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

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

因此,对于列出的容器,不仅从end()返回的迭代器应该是decrementable,而且decrementiterator也应该是可撤销引用的。当然,除非容器是空的。调用未定义行为)。

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:

实际上,带有Visual c++编译器的vector、list和deque实现与表完全一样。当然,这并不意味着每个编译器都这样做:

// 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

ISO/ iec14882:2003 c++标准17.3.1.2/6 -要求

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.

在某些情况下,语义需求被表示为c++代码。这些代码的目的是作为构造到另一个构造的等价性的规范,而不一定是构造必须实现的方式。

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.

因此,虽然实现可能不会按照begin()和end()实现这些表达式,但是c++标准指定这两个表达式是等价的。换句话说,a.back()和*- a.end()是根据上述子句的等价构造。在我看来,这意味着您应该能够用*- a.end()替换每个a.o back()实例,反之亦然。


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

根据Bo Persson的说法,我手头的c++标准的修订有一个关于表68的缺陷。

Proposed resolution:

建议解决方法:

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

将表68中的“可选序列操作”中的规格从23.1.1/12更改为“a.back()”

*--a.end()

to

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

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

以及“a.pop_back()”的规范

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.

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

#2


0  

This code is not OK, in case list is empty you are in trouble.

这段代码不行,如果列表为空,你就有麻烦了。

So check for that, if list is not empty the code is very fine.

检查一下,如果列表不是空的代码很好。