我可以在迭代 std::string 对象时删除它中的元素吗?



我可以在迭代 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_backitr可能会失效,但我们会在每次迭代中重新获取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[]atfrontbackbeginrbeginendrend

替代方法是使用结果或erase()将有效的迭代器返回到当前位置。或者,您可能希望将erase()std::remove_if()结合使用,以首先有效地重新调整字符串,然后删除实际内容。

最新更新