Для идиомы erase-remove, зачем нужен второй параметр, который указывает на конец контейнера? - PullRequest
17 голосов
/ 11 мая 2019

Рассмотрим следующий код (взят из cppreference.com , слегка адаптирован):

#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>

int main()
{
    std::string str1 = "     Text with some   spaces";
    str1.erase(std::remove(str1.begin(), str1.end(), ' '), str1.end());
    std::cout << str1 << '\n';

    return 0;
}

Почему второй параметр erase необходим? (Т.е. str1.end() в данном случае.)

Почему я не могу просто указать итераторы, которые возвращаются remove в erase? Почему я должен сказать ему также о последнем элементе контейнера, из которого нужно стереть?

Подводный камень в том, что вы также можете вызвать erase без второго параметра, но это, очевидно, дает неверный результат.

Есть ли случаи, когда я не хотел бы передавать конец контейнера в качестве второго параметра в erase?

Опускание второго параметра erase для идиомы erase-remove всегда является ошибкой или это может быть правильным решением?

1 Ответ

18 голосов
/ 11 мая 2019

std::remove возвращает один итератор;это новый последний итератор для последовательности.Но когда последовательность управляется контейнером, размер контейнера не изменился;std::remove перетасовывает порядок элементов в последовательности, но фактически не удаляет ни один из них.

Чтобы избавиться от элементов в контейнере, которые не являются частью новой последовательности, которую вы вызываете,конечно, container.erase().Но цель состоит в том, чтобы удалить все лишние элементы;вызов container.erase() только с одним итератором говорит ему удалить этот элемент .Чтобы сказать container.erase() стереть все «отсюда до конца», вы должны сказать ему оба где «здесь» и где конец.Таким образом, это означает два итератора.

Если это поможет, подумайте об идиоме «удалить / стереть» как два отдельных шага:

auto new_end = std::remove(str1.begin(), str1.end(), ' ');
str1.erase(new_end, str1.end());
...