Итерация по std :: set <unique_ptr>, как отследить, какие из них удалить? - PullRequest
0 голосов
/ 28 августа 2018

Мне нужно перебрать некоторые объекты class T.

Они хранятся в std::set<std::unique_ptr<T>> tees.

Основная цель тела цикла - использовать объекты, но при этом я также выясню, когда некоторые объекты больше не нужны и могут быть удалены.

Я использую цикл for на основе диапазона для итерации по unique_ptrs:

for (std::unique_ptr<T> & tee : tees)

Я знаю, что не могу вызвать tees.erase (tee) внутри цикла (UB). Поэтому я должен собрать unique_ptr s, которые нужно удалить в коллекции помощников. Проблема: указатели уникальны, поэтому я не могу скопировать их в коллекцию помощников.

Я мог бы собрать необработанные указатели в std::set<T*>, но как бы я использовал их после цикла, чтобы удалить соответствующие unique_ptr s из коллекции tees? Кроме того, сбор необработанных указателей снова почему-то кажется неправильным, когда я попытался использовать умные указатели в этой проблеме.

Я мог бы переключиться на shared_ptr, но указатели будут когда-либо использоваться совместно только с целью удаления объектов. Не чувствует себя хорошо.

Я мог бы переключиться с диапазона на диапазон на что-то другое, например, самостоятельно обработать итераторы и получить следующий итератор перед удалением записи. Но возвращение к методикам до C ++ 11 также не кажется правильным.

Я мог бы переключиться на std :: remove_if. ( EDIT : На самом деле я не могу. Объясняется в комментариях под этим вопросом и под принятым ответом.) Тело цикла переместится в лямбду unary_predicate. Но основная цель цикла состоит не в том, чтобы определить, следует ли удалять объекты, а в том, чтобы использовать их, изменяя их.

Кажется, что путь наименьшего сопротивления - вернуться к обработке итераторов, поэтому мне даже не нужна коллекция помощников. Но мне интересно, можете ли вы помочь мне с решением C ++ 11-иш (или 14,17)?

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

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

int counter =0;
vector<int> indices;
for (unique_ptr<T> & tee : tees)
{
    if (bCondition)
        indices.push_back(counter);
    counter++;
}

reverse(indices.begin(), indices.end());
for (int  i : indices)
    tees.erase(tees.begin() + i);
0 голосов
/ 28 августа 2018

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

Так что вы можете сделать что-то вроде этого:

template<typename Cont, typename Pred>
void erase_if(Cont& c, decltype(std::begin(c)) b, decltype(std::end(c)) e, Pred p)
{
    while(b != e)
    {
        if(p(*b))
            b = c.erase(b);
        else
            ++b;
    }
}

template<typename Cont, typename Pred>
void erase_if(Cont& c, Pred p)
    { erase_if(c, std::begin(c), std::end(c), p); }

Тогда назовите это что-то вроде:

erase_if(tees, [](std::unique_ptr<int> const& up){
    // use up here...
    return (*up) & 1; // erase if odd number
});

или

erase_if(tees, std::begin(tees), std::end(tees), [](std::unique_ptr<int> const& up){
    // use up here...
    return (*up) & 1; // erase if odd number
});
0 голосов
/ 28 августа 2018

Не думаю, что вы найдете что-нибудь проще, чем

for(auto it = container.begin(), it != container.end();)
{
    //use *it here
    if(needs_to_be_erased)
        it = container.erase(it);
    else
        ++it;
}

, поскольку std::set не обеспечивает изменяемый доступ к его элементам, любой тип transform или remove не будет работать. Вам нужно будет создать контейнер итераторов, а затем после обработки набора пройти через этот контейнер итераторов, вызывая для каждого из них erase.

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