Использует ли функция стирания в строке недействительные итераторы

У меня есть следующий код, который принимает строку и стирает не алфавитные символы

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

}
}

Я показал это своему профессору, и он сказал мне, что делать это рискованно, потому что это может сделать недействительным используемый мной итератор. Тем не менее, я думал, что стирание только сделает недействительными итераторы после точки стирания, и я позаботился о том, чтобы не использовать никаких итераторов после этой точки.
Так может ли этот код вылетать или вызывать неопределенное поведение?

1

Решение

std::vector::erase работает так, как вы предлагаете; он делает недействительными только итераторы, начиная с первого стертого элемента. Однако это не относится к std::string,

C ++ позволяет аннулировать строковые итераторы без промедления.

Стандарт C ++ традиционно был более гибким с требованиями к std::string, (Или, другими словами, он традиционно позволял разработчикам использовать оптимизации, которые не были бы действительны для векторов.) std::string::eraseи другие строковые мутаторы.

В [string.require] (§21.4.1 из n3797), стандарт допускает, что:

  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--);
}
}
1

Другие решения

Это не скомпилируется. str это std :: string и str == ' ' два разных типа.

Во-вторых, при повторной итерации необходимо использовать string :: reverse_iterator, string :: rbegin () и string :: rend ();

1

По вопросам рекламы [email protected]