是这种减少的测序调用不确定的行为

Is the sequencing of this decrement invoking undefined behaviour?

本文关键字:调用 不确定      更新时间:2023-10-16

我正在寻找确认,澄清是该代码定义是否很好。

通过将迭代器重新分配给容器的erase()函数的结果,将容器的元素删除在循环中。循环通常有点混乱:

struct peer { int i; peer(int i): i(i) {} };
int main()
{
    std::list<peer> peers {0, 1, 2, 3, 4, 5, 6};
    for(auto p = peers.begin(); p != peers.end();) // remember not to increment
    {
        if(p->i > 1 && p->i < 4)
            p = peers.erase(p);
        else
            ++p; // remember to increment
    }
    for(auto&& peer: peers)
        std::cout << peer.i << ' ';
    std::cout << 'n';
}

输出: 0 1 4 5 6

在我看来,以下情况可能会更加整洁,假设它没有调用不确定的行为:

struct peer { int i; peer(int i): i(i) {} };
int main()
{
    std::list<peer> peers {0, 1, 2, 3, 4, 5, 6};
    for(auto p = peers.begin(); p != peers.end(); ++p)
        if(p->i > 1 && p->i < 4)
            --(p = peers.erase(p)); // idiomatic decrement ???
    for(auto&& peer: peers)
        std::cout << peer.i << ' ';
    std::cout << 'n';
}

输出: 0 1 4 5 6

我认为这项工作的原因如下:

  • peers.erase()将始终返回增量的p,因此再次将其降低是安全的

  • peers.erase(p)使p的A 复制,因此由于测序参考

  • ,它不会在错误的值上运行
  • p = peers.erase(p)返回 p&,因此减少在正确的对象引用上运行。

,但我有疑虑的怀疑。我担心通过在同一表达式中使用p在CC_9中用作参数,尽管它看起来还可以。

任何人都可以在这里看到我的评估问题吗?还是定义得当?

它取决于您用于检测这些元素删除的条件。如果您尝试删除第一个元素,它将失败,因为erase将返回新的begin(),然后您将其降低。这是非法的,即使您立即将其再次增加。

为了避免此错误,并且由于它更常见和可读,所以我坚持第一个版本。

第二版是 - 正如@danielfrey所说的 - 错误,但是如果您不喜欢第一个版本,为什么不做这样的事情:

std::list<int> myList = { 0, 1, 2, 3, 4, 5, 6 };
myList.remove_if(
    [](int value) -> bool {
        return value > 1 && value < 4;
    }
);
/* even shorter version
myList.remove_if([](int value) -> bool {
    return (value > 1 && value < 4);
});
*/
for(int value : myList) {
    std::cout << value << " ";
}

这给出输出:

0 1 4 5 6

实时示例。