#c #range
#c #диапазон
Вопрос:
Я сталкиваюсь с распространенной проблемой в своем коде, когда я хотел бы удалить только один элемент из перевернутого std::vector после того, как он удовлетворяет предикату. Я понимаю, что есть несколько способов сделать это с помощью ranges-v3, но каждый из способов, которые я придумываю, кажется немного запутанным.
Вот пример целевого вектора v:
std::vector v = { 1, 2, 3, 2, 4 };
Результатом должен быть вектор r:
std::vector r = { 1, 2, 3, 4 };
Это будет сделано путем удаления первых 2 (через лямбда-предикат «is_two»), который обнаруживается при обратном обходе вектора v.
Вот один из способов, как это может выглядеть в цикле vanilla C raw:
auto is_two = [](int a) { return a == 2; };
for (int i = v.size(); --i >= 0;) {
if (is_two(v[i])) {
v.erase(v.begin() i);
break;
}
}
Вот мои плохие диапазоны -версия v3:
namespace rs = ranges;
namespace rv = ranges::view;
namespace ra = ranges::action;
rs::for_each(v | rv::enumerate
| rv::reverse
| rv::filter([](auto i_e) { return i_e.second == 2; })
| rv::take(1),
[amp;](autoamp; i_e) { v.erase(v.begin() i_e.first); });
В идеале мне интересно, есть ли какое-то решение, которое могло бы выглядеть примерно так:
ra::remove_if(v | rv::reverse, is_two);
Чтобы обобщить, я хотел бы знать, как можно взять контейнер, передать его через некоторые операции ranges::view, а затем удалить элементы в результирующем диапазоне из исходного контейнера.
Комментарии:
1. Ключевая проблема заключается в том, как преобразовать итератор в представлении в соответствующий итератор исходного контейнера.
Ответ №1:
Поскольку, похоже, никто не придумал лучшего подхода, я хотел бы упомянуть для вашей пользы возможность прибегнуть к старому доброму reverse_iterator
s.
vec.erase(std::prev(ranges::find_if(vec.rbegin(), vec.rend(), is_two).base()));
По общему признанию, это не очень похоже на ranges,
но, по крайней мере, это работает.
Комментарии:
1. На самом деле это лучше, чем мое решение в моем случае использования, спасибо. Не могли бы вы объяснить, что делает .base() ? Еще раз спасибо.
2. @IanCaburian На странице
reverse_iterator
cppreference подробно объясняется это.base()
возвращает базовый итератор, который отключен на 1 по отношению к фактической позиции, следовательно.prev
Ответ №2:
Основная цель циклов for на основе диапазона — согласованность. Для каждого элемента выполняется одна и та же операция.
Удаление элемента нарушает эту согласованность. Таким образом, лучшим решением является обычный цикл for, в котором вы можете использовать итераторы и разрывы.
Когда у вас есть молоток, все выглядит как гвоздь. Не превращайте цикл ranged в обычный.