为什么 std::set.erase(first, last) 会影响从中获取 (first, last) 的容器?

Why does std::set.erase(first, last) impact the container from which (first, last) were taken?

我先声明,我对 C++ 总体上还是个新手。最近,我 运行 从 std::set.erase() 方法中发现了一些令人费解的行为,我已将其隔离到以下代码中。此代码示例在遇到第二个 for 循环时因分段错误而崩溃:

#include <set>
#include <iostream>

int main() {
    std::set<int> foo = {0, 1, 2};
    std::set<int> bar = {0, 1, 2, 3, 4};
    for(int i : foo) {
        printf("%d\n", i);
    }
    bar.erase(foo.begin(), foo.end());
    for(int i : foo) { //Crash happens right here, before entering the body of the loop.
        printf("%d\n", i);
    }
    return 0;
}

而如果您删除对 erase() 的调用,它不会崩溃。

这有点令人惊讶。调用 bar.erase() 显然应该修改 "bar" 但我通常不认为它会对 "foo" 的功能产生任何影响。 std::set.erase() 做了什么导致出现分段错误?

这很简单,只需创建 "foo" 的副本以向 erase() 提供开始和结束迭代器即可绕过,但我很好奇为什么首先会发生这种行为。

擦除函数的参数是迭代器。迭代器不可跨对象移植。您传递给 bar.erase 的迭代器应该是 bar 上的迭代器。如果不是,你会得到未定义的行为。

崩溃的发生可能是因为对 bar.erase 的调用以某种方式删除了 foo 的内容,因为它使用了基于 foo 的迭代器,但 foo 认为它们仍然存在。

您不应创建 foo 的副本来为 bar.erase 提供迭代器。您应该使用基于 bar 的迭代器。

您正在将迭代器从 foo 传递到 bar 上的 erase 调用中。 erase 的迭代器对重载擦除该对表示的范围,而不是该范围内出现的每个值。因此,它仅在您传递调用 erase 的集合的有效范围时才有效。如果你想从另一个集合中删除所有元素,你可以使用 std::set_difference:

bar = [&] {
    std::set<int> result;
    std::set_difference(bar.begin(), bar.end(), foo.begin(), foo.end(), std::inserter(result));
    return result;
}();