Гарантируется ли std::remove_if вызывать предикат по порядку?
Будет ли std::remove_if всегда вызывать предикат для каждого элемента по порядку (в соответствии с порядком итератора) или он может вызываться не по порядку?
Вот игрушечный пример того, что я хотел бы сделать:
void processVector(std::vector<int> values)
{
values.erase(std::remove_if(values.begin(), values.end(), [](int v)
{
if (v % 2 == 0)
{
std::cout << v << "\n";
return true;
}
return false;
}));
}
Мне нужно обработать и удалить все элементы вектора, которые соответствуют определенным критериям, и erase + remove_if кажется идеальным для этого. Однако обработка, которую я буду выполнять, имеет побочные эффекты, и мне нужно убедиться, что обработка происходит по порядку (в примере с игрушкой, предположим, что я хочу напечатать значения в порядке их появления в исходном векторе).
Можно ли предположить, что мой предикат будет вызываться для каждого элемента по порядку?
Я предполагаю, что политики выполнения C++17 устранят это неоднозначно, но, поскольку C++17 еще не вышел, это, очевидно, мне не поможет.
Редактировать: Кроме того, это хорошая идея? Или есть лучший способ сделать это?
3 ответа
Стандарт не дает никаких гарантий порядка вызова предиката.
То, что вы должны использовать, это stable_partition
, Вы разделяете последовательность на основе вашего предиката. Затем вы можете пройти секционированную последовательность, чтобы выполнить любой "побочный эффект", который вы хотели сделать, так как stable_partition
обеспечивает относительный порядок обоих наборов данных. Затем вы можете стереть элементы из vector
,
stable_partition
должен быть использован здесь, потому что erase_if
оставляет содержимое "стертых" элементов неопределенным.
В коде:
void processVector(std::vector<int> values)
{
auto it = std::stable_partition(begin(values), end(values), [](int v) {return v % 2 != 0;});
std::for_each(it, end(values), [](int v) {std::cout << v << "\n";});
values.erase(it, end(values));
}
Немного опоздал на вечеринку, но вот мое мнение:
Хотя порядок не указан, он будет включать в себя прыжки через обручи для реализации порядка, отличного от первого к последнему, из-за следующего:
- Сложность указана как "точно
std::distance(first, last)
применения предиката", что требует посещения каждого элемента ровно один раз. - Итераторы
ForwardIterator
s, что означает, что их можно только увеличивать. - [C++17 и выше] Чтобы предотвратить параллельную обработку, можно использовать версию, которая принимает политику выполнения, и передать
std::execution::seq
.
Учитывая вышеизложенное, я считаю, что (непараллельная) реализация, которая следует другому порядку, будет запутанной и не будет иметь преимуществ перед простым случаем.
Источник: https://en.cppreference.com/w/cpp/algorithm/remove
Они должны быть обработаны по порядку, но это не гарантировано.
std::remove_if()
перемещает "удаленные" элементы в конец контейнера, они фактически не удаляются из контейнера, пока erase()
называется. Обе операции могут сделать недействительными существующие итераторы в std::vector
,