在字符串中使用 erase 函数是否会使迭代器失效
Does using the erase function in a string invalidate iterators
我有以下代码,它接受一个字符串并删除非字母字符
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)中,该标准接受:
- 引用
basic_string
序列元素的引用、指针和迭代器可能会因该basic_string
对象的以下用法而失效:- 作为任何标准库函数的参数,将对非常量
basic_string
的引用作为参数。 - 调用非常量成员函数,除了
operator[]
、at
、front
、back
、begin
、rbegin
、end
和rend
之外。
- 作为任何标准库函数的参数,将对非常量
换句话说,调用像 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--);
}
}
相关文章:
- std::vector::迭代器是否可以合法地作为指针
- 是否应避免从非常量迭代器转换为常量迭代器?
- 钳制迭代器是否有效
- 测试迭代器是否位于列表中的最后一个
- 迭代器在递增和递减时是否保证会变异?
- 是否可以(重新)分配来自不同容器的迭代器?
- 调用 erase() 函数是否也会在擦除元素之前更改迭代器值?
- 迭代器是否分配内存(如指针)?
- 在擦除或修改作为不同索引键的值时,boost::multi_index 迭代器是否无效?
- 使用自己的迭代器分配容器是否安全?
- 对于C字符串,是否有一个标准的C++迭代器
- 我当前实现的双向链表类是否需要重构迭代器 end() 功能?
- 在C++中,是否可以在单个循环中顺序迭代多个迭代器
- 如果我的容器位于两个现有值之间,那么伪造迭代器类别是否合理
- 是否可以重用通常的迭代器来构建常量迭代器?
- 迭代器的范围 TS 和 C++20 概念是否需要能够使用"运算符>"?
- 是否可以创建虚拟迭代器类?
- 输入迭代器是否可以仅在赋值的右侧符号上取消引用?
- 由迭代器构造的反向迭代器是否是其以前的迭代器?
- 检查迭代器是否具有我们要在C++中分配的值是否明智的优化