我可以在迭代 std::string 对象时删除它中的元素吗?
for (auto itr = str.rbegin(), rend = str.rend(); itr != str.rend() && *itr == '0'; ++itr)
{
str.pop_back();
}
不,这是不允许的。修改字符串的内容会使任何迭代器失效,尤其是 itr
。
要从字符串中删除尾随字符,请考虑使用 find_last_not_of
:
auto ix = str.find_last_not_of('0');
str.resize(ix + 1);
另一种选择是使用 erase
函数,它将返回序列中的下一个迭代器,从而避免任何无效的迭代器。
for (auto itr = str.rbegin(); itr != str.rend() && *itr == '0'; /*nothing*/)
itr = str.erase(itr);
这将删除最后一个字符(就像pop_back
一样)并安全地推进迭代器,因此您实际上再也不会有无效的迭代器了。需要注意的是,您不能使用之前计算的rend
迭代器,因为它是无效的;但是,您实际上并没有使用它。
您可以在迭代字符串时删除元素,但您需要以略微不同的方式编写循环才能安全地执行此操作。在这种情况下,我们只真正关心字符串中的最后一个字符,因此我们可以执行以下操作:
for (auto itr = str.rbegin();
itr != str.rend() && *itr == '0';
itr=str.rbegin())
{
str.pop_back();
}
尽管当我们对字符串进行pop_back
时itr
可能会失效,但我们会在每次迭代中重新获取str.rbegin()
的值,这保证每次调用时都会给出一个有效的反向迭代器。不过,我们剩下的东西不再真正很好地利用for
循环——我们不妨使用while
循环:
while (str.rbegin() != str.rend() && *str.rbegin() == '0')
str.pop_back();
我想我宁愿这样写:
while (!str.empty() && *str.rbegin() == '0')
str.pop_back();
。或(使用稍微干净一点的等效*str.rbegin()
:
while (!str.empty() && str.back() == '0')
str.pop_back();
根据string::pop_back
http://www.cplusplus.com/reference/string/string/pop_back/的参考
与此对象相关的任何迭代器、指针和引用都可能是 失效。
所以我想你不能在带有迭代器的 for 循环中做到这一点。
你这样做:
while ( str.size() > 0 && str[ str.size()-1] == '0' ] )
str.pop_back();
或者,您可以使用计数器而不是迭代器来执行 for 循环。
根据圣经,21.4.1章,第6节:
6 引用、指针和迭代器,引用basic_string序列可能因以下用途而无效basic_string对象:
— 作为任何标准库函数的参数以引用非常量basic_string作为参数。
— 调用非常量成员函数,运算符 [] 除外,在,前面,后面,开始,开始、结束和结束。
所以我会说"不,不使用pop_back"。
但是,您当然可以使用返回迭代器的erase
重载,并使用该迭代器而不是您擦除的迭代器,就像其他容器一样。
根据标准,在std::basic_string
上使用s.erase()
会使所有指向s
的指针、引用和迭代器失效。标准中的相关部分是 21.4.1 [string.require] 第 6 段:
引用
basic_string
序列元素的引用、指针和迭代器可能会因该basic_string
对象的以下用法而失效:
- 作为对 Anys tandard 库函数的参数,将引用非
const
basic_string
作为参数。- 调用非
const
成员函数,除了operator[]
、at
、front
、back
、begin
、rbegin
、end
和rend
。
替代方法是使用结果或erase()
将有效的迭代器返回到当前位置。或者,您可能希望将erase()
与std::remove_if()
结合使用,以首先有效地重新调整字符串,然后删除实际内容。