擦除结束迭代器是对标准的疏忽还是设计决策?

Is erasing the end iterator an oversight in the standard or a design decision?

标准库容器允许我们 erase 由迭代器 firstlast.

表示的范围
std::vector<foo> bar;
//         first it     last it
bar.erase(bar.begin(), bar.end());

标准规定 first 迭代器必须有效 可解引用,而 last 只需要有效。但是,如果 first == lastfirst 不需要取消引用,因为 erase 是空操作。这意味着以下是合法的:

bar.erase(bar.end(), bar.end());

但是,如果我只想删除一个元素而不是一个范围,则迭代器必须有效 并且 可取消引用,从而导致以下未定义的行为:

bar.erase(bar.end());

为什么这不只是空操作?这是标准委员会的疏忽,将在未来的语言修订版中解决,还是我没有看到重点的故意设计决定?

据我所知,它没有任何好处,但在执行以下操作时会造成额外的麻烦:

bar.erase(std::find(bar.begin(), bar.end(), thing));

C++ 习惯于在其标准库中保留任何额外的工作,也称为 "You don't pay for what you don't use"。在这种情况下,单迭代器版本需要将迭代器与结束迭代器进行比较。对于大多数情况,此检查是多余的,这是未使用的额外工作,尽管工作量很小。

erase(iterator it):
    if it != end: // we can't avoid this check now
        // erase element

在超载取范围的情况下,在结束前自然停止。一个简单的实现可能如下所示:

erase(iterator first, iterator last):
    while first != last:
        erase(first++);

它需要一些 方法来知道何时停止擦除。在某些情况下,它可能会更聪明,例如 memmove 在不分支的情况下用一块内存覆盖已擦除的内存,但这只会在特定情况下发生。


另请注意,与其他方式相比,由此构建检查版本要容易得多:

checked_erase(Container cont, iterator it):
    if it != cont.end():
        cont.erase(it);

Why isn't this just a no-op?

为什么会是空操作?您正在调用专门设计用于删除单个元素的方法,并以无法删除任何元素的方式调用它。那没有任何意义。您可能会争辩说它应该抛出异常而不是具有未定义的行为,但是我没有在您的问题中看到任何有效的论据来将其明确定义为什么都不做,因此我认为没有理由认为这可能是一种疏忽。

你的例子对我来说没有说服力:只要该项目在向量中是唯一的,它已经很容易用 std::remove 而不是 std::find 的简单形式表示。如果该项目不是唯一的,则更明确地说明您希望删除多少项目。

换个角度看:erase(it) 等同于所有迭代器的 erase(it, it+1)。包括结束迭代器。为结束迭代器明确定义 erase(it)erase(it, it+1) 未定义的特殊例外会引入不一致。