在字符串中使用 erase 函数是否会使迭代器失效

Does using the erase function in a string invalidate iterators

本文关键字:是否 迭代器 失效 函数 erase 字符串      更新时间:2023-10-16

我有以下代码,它接受一个字符串并删除非字母字符

void removeNonAlpha(string& str){
    for (string::iterator it = str.begin(); it < str.end(); it++){
        if (!(isUpperCaseLetter(*it) || isLowerCaseLetter(*it) || str == ' '))  
            str.erase(it--);
    }
}

向我的教授展示了这个,他告诉我这样做是有风险的,因为它可能会使我正在使用的迭代器无效。但是,我认为擦除只会在擦除点之后使迭代器失效,并且我确保在该点之后不使用任何迭代器。那么这段代码会崩溃或导致任何未定义的行为吗?

std::vector::erase按照

您的建议工作;它只会使从第一个擦除的元素开始的迭代器失效。但是,这不适用于std::string

C++允许字符串迭代器在帽子掉落时失效。

传统上,C++标准对std::string的要求更加灵活。(或者,换句话说,它传统上允许实现者使用对向量无效的优化。std::string::erase 和其他字符串突变体也是如此。

[string.require](n3797 的 §21.4.1)中,该标准接受:

    引用
  1. basic_string序列元素的引用、指针和迭代器可能会因该basic_string对象的以下用法而失效:
    • 作为任何标准库函数的参数,将对非常量basic_string的引用作为参数。
    • 调用非常量成员函数,除了 operator[]atfrontbackbeginrbeginendrend 之外。

换句话说,调用像 std::string::erase 这样的潜在变异函数可能会使该字符串的所有迭代器无效,即使没有对字符串进行可见的修改(例如,因为要擦除的范围为空)。

(最新的C++标准草案具有相同的措辞,尽管现在是第4段。

如果字符串的第一个字符不是字母,则建议的代码涉及未定义的行为。

在字符串的第一个循环中,迭代器 it 的值为 str.begin() 。该迭代器不能递减,因为结果不会在字符串内。因此,递增递减的迭代器可能不会在下一次迭代中返回it str.begin()

使用索引而不是迭代器

以上都不适用于整数位置索引。因此,如果您可以安全地将循环替换为非常相似的循环:

void removeNonAlpha(string& str){
    for (auto sz = str.size(), i = 0; i < sz; ++i){
        if (!(isUpperCaseLetter(str[i]) ||
              isLowerCaseLetter(str[i]) ||
              str[i] == ' '))  
            str.erase(i--);
    }
}