【问题标题】:Does --list.begin() == list.end()?--list.begin() == list.end() 吗?
【发布时间】:2020-08-26 19:35:57
【问题描述】:

标准是否保证

--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() 持有;那怎么办?

【问题讨论】:

标签: c++ iterator iteration language-lawyer stdlist


【解决方案1】:

标准是否保证

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

不,标准不保证std::list (也不能假设给定双向迭代器)。事实上,--list.begin() 的行为是未定义的。

这也需要 ++list.end() == list.begin() 来持有;那怎么样?

也不能保证,++list.end() 具有未定义的行为。


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

[双向.迭代器]

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

[输入.迭代器]

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;
}

【讨论】:

  • --list.begin() 的行为几乎可以肯定是未定义的,但编译器可能会对其进行一些明智的处理,以使调试更容易。完全取决于编译器供应商。
  • @Hi-IloveSO 如果这是最后剩下的元素怎么办?然后i-- 将超出范围的开头。
  • @eerorika 再想一想;它实际上永远不会。如果i 是第一个元素,list.erase( i++ ); 将删除该元素,i 将成为下一个元素,最多 list.end(),而i-- 将退后一步,允许i++ 在第三个for-loop 表达式中很好。
  • @Hi-IloveSO 如果你删除最后一个元素,i == end() == begin()i--i == begin() 未定义时。
  • @Hi-IloveSO End can never equal begin 你的假设是错误的。当列表为空时,结束总是等于开始。当您擦除最后一个剩余元素时会发生这种情况。
【解决方案2】:

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

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

但请注意,--list.begin() 的行为是未定义的,这就是为什么这个特定的实现是一个可能的选择。

【讨论】:

  • “确实成立”如果您禁用检查。
  • @MarcGlisse:很可能。快速查看源代码,看起来他们使用了一个额外的节点,并且反过来绊倒它会使迭代器检查失败。真的很聪明。
  • en.wikipedia.org/wiki/Sentinel_node 如果您想要这个额外节点的名称。
猜你喜欢
  • 2011-05-27
  • 2019-05-22
  • 2013-02-13
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-01
相关资源
最近更新 更多