Лучший способ удалить элементы из контейнера, используя его версию диапазонов - PullRequest
3 голосов
/ 22 апреля 2019

Я сталкиваюсь с общей проблемой в моем коде, когда я хотел бы удалить только один элемент из обращенного std :: vector после того, как он удовлетворяет предикату.Я понимаю, что есть несколько способов сделать это с range-v3, но каждый способ, который я придумаю, кажется немного запутанным.

Вот пример целевого вектора v:

std::vector v = { 1, 2, 3, 2, 4 };

Результатом должен быть вектор r:

std::vector r = { 1, 2, 3, 4 };

, что будет сделано путем удаления первых 2 (с помощью лямбда-предиката "is_two"), которыйобнаруживается при обратном обходе вектора v.

Вот один из примеров того, как он может выглядеть в ванильном цикле 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;
    }
}

Вот моя плохая версия range-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),

            [&](auto& i_e) { v.erase(v.begin() + i_e.first); });

В идеале мне интересно, есть ли какое-нибудь решение, которое могло бы выглядеть примерно так:

ra::remove_if(v | rv::reverse, is_two);

В общем, я хотел бы знать, как можно взять контейнер, передайте его через некоторые операции range :: view, затем удалите элементы в результирующем диапазоне из исходного контейнера.

Ответы [ 2 ]

1 голос
/ 22 апреля 2019

Основное назначение циклов на основе диапазона - согласованность. Выполнение одинаковой операции для каждого элемента.

Удаление элемента нарушает эту последовательность. Поэтому лучшим решением является обычный цикл for, в котором вы можете использовать итераторы и разрывы.

Когда у тебя молоток, все выглядит как гвоздь. Не вбивайте петлю дальнего боя в нормальную.

1 голос
/ 22 апреля 2019

Поскольку никто, похоже, не придумал лучшего подхода, я хотел бы упомянуть в вашу пользу возможность прибегнуть к старым добрым reverse_iterator с.

vec.erase(std::prev(ranges::find_if(vec.rbegin(), vec.rend(), is_two).base()));

По общему признанию, это не очень большой диапазон, но по крайней мере это работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...