--list.begin== list.end吗?

问题描述

执行标准保证

--list.begin() == list.end()

始终成立,并且它实际上是有效的操作吗? (liststd::list的实例)。

至少MSVC 2019似乎是这种情况。

这在以下情况下非常有用:

for ( auto i = list.begin(); i != list.end(); ++i )
{
  ...
  list.erase(i--);
  ...
}

,即在迭代时删除元素时,因为i可能是列表的开头。这也需要++list.end() == list.begin()来保留;那怎么办?

编辑:核心问题实际上是list.erase(i--);可能在嵌套范围/ if else结构中,甚至在switch情况下,这使得else语句的等效项实际上与上述条件相同,真的很丑。通过允许对列表的闭环表示进行迭代,不会引入不连续性,并且每个人都很高兴。

Edit2:控制流问题;考虑

for ( auto i = list.begin(); i != list.end(); ++i )
{
  if ( X + Y == W )
  {
    if ( X || Y && Z )
    {
      list.erase( i-- );
    }
    my_function( Y );
  }
  else
  {
    list.erase( i-- );
  }
  ...
}

vs

for ( auto i = list.begin(); i != list.end(); )
{
  bool should_increment = true;

  if ( X + Y == W )
  {
    if ( X || Y && Z )
    {
      i = list.erase( i );
      should_increment = false;
    }
    my_function( Y );
  }
  else
  {
    i = list.erase( i );
    should_increment = false;
  }
  ...
  if ( should_increment )
  {
    i++;
  }
}

解决方法

执行标准保证

--list.begin() == list.end()

否,标准不能保证std::list的情况(在给出双向迭代器的情况下也不能假定这种保证)。实际上,--list.begin()的行为是不确定的。

这还需要++ list.end()== list.begin()来保存;那怎么办?

也不保证,++list.end()的行为不确定。


[语言律师]的标准报价:

[bidirectional.iterators]

Expression | Operational   | Assertion/note
           | semantics     | pre-/post-condition
---------------------------------------------------------
--r        |               | Preconditions:
           |               |  there exists s such that r == ++s.
---------------------------------------------------------
r--        | { X tmp = r;  |
           | --r;          |
           | return tmp; } |

[input.iterators]

Expression | Operational   | Assertion/note
           | semantics     | pre-/post-condition
---------------------------------------------------------
++r        |               | Preconditions:
           |               |  r is dereferenceable.

问题是不满足前提条件。


我对编写具体示例的建议。这与稍有不同,不同之处在于,在实际上从列表中删除元素之前将调用my_function

list.remove_if([](int n) {
    if ( X + Y == W )
    {
        my_function( Y );
        return X || Y && Z;
    }
    return true;
});

如果需要确切的行为,则可以使用以下代码:

for (auto it = list.begin(),last = list.end(); it != last;)
{
    auto next = it;
    ++next;
    
    if ( X + Y == W )
    {
        if ( X || Y && Z )
        {
            list.erase( it );
        }
        my_function( Y );
    }
    else
    {
        list.erase( it );
    }
    
    it = next;
}
,

当前的MSVC C ++标准库将std::list实现为循环的双向链接列表,并有一个额外的节点充当第一个元素与最后一个元素之间的耦合器。

所以--list.begin() == list.end()确实成立。

但是请注意--list.begin()的行为是不确定的,这就是为什么这种特殊实现是可能的选择的原因。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...