Что не так с моим вектором <T>:: стереть здесь? - PullRequest
1 голос
/ 01 февраля 2012

У меня есть две vector<T> в моей программе, которые называются active и non_active соответственно. Это относится к объектам, которые он содержит, относительно того, используются ли они или нет.

У меня есть некоторый код, который зацикливает вектор active и проверяет любые объекты, которые могли бы стать неактивными. Я добавляю их к temp_list внутри цикла.

Затем после цикла, я беру свой temp_list и выполняю non_active.insert всех элементов в temp_list.

После этого я вызываю erase на моем active векторе и передаю temp_list для удаления.

По какой-то причине, однако, erase вылетает.

Это код:

non_active.insert(non_active.begin(), temp_list.begin(), temp_list.end());
active.erase(temp_list.begin(), temp_list.end());

Я получил это утверждение:

Expression:("_Pvector == NULL || (((_Myvec*)_Pvector)->_Myfirst <= _Ptr && _Ptr <= ((_Myvect*)_Pvector)->_Mylast)",0)

Я посмотрел онлайн и увидел, что есть идиома удаления-удаления, однако не уверен, как бы я применил это для удаления диапазона элементов из vector<T>

Я не использую C ++ 11.

Ответы [ 7 ]

8 голосов
/ 01 февраля 2012

erase ожидает диапазон переданных итераторов, которые лежат в текущем векторе.Вы не можете передать итераторы, полученные из другого вектора, в erase.

Вот возможное, но неэффективное решение C ++ 11, поддерживаемое лямбдами:

active.erase(std::remove_if(active.begin(), active.end(), [](const T& x)
{
    return std::find(temp_list.begin(), temp_list.end(), x) != temp_list.end();
}), active.end());

И вотэквивалентное решение C ++ 03 без лямбды:

template<typename Container>
class element_of
{
    Container& container;

    element_of(Container& container) : container(container) {}

public:

    template<typename T>
    bool operator()(const T& x) const
    {
        return std::find(container.begin(), container.end(), x)
            != container.end();
    }
};

// ...

active.erase(std::remove_if(active.begin(), active.end(),
                            element_of<std::vector<T> >(temp_list)),
             active.end());

Если вы замените temp_list на std::set и std::find_if на вызов функции-члена find на наборе, производительность должнабыть приемлемым.

3 голосов
/ 01 февраля 2012

Вы действительно можете использовать идиому стирания / удаления для вашего случая.Вам просто нужно переместить значение в другой контейнер до того, как std::remove_if возможно перетасует его: в предикате.

template<class OutIt, class Pred>
struct copy_if_predicate{
  copy_if_predicate(OutIt dest, Pred p)
    : dest(dest), pred(p) {}

  template<class T>
  bool operator()(T const& v){
    if(pred(v)){
      *dest++ = v;
      return true;
    }
    return false;
  }

  OutIt dest;
  Pred pred;
};

template<class OutIt, class Pred>
copy_if_predicate<OutIt,Pred> copy_if_pred(OutIt dest, Pred pred){
  return copy_if_predicate<OutIt,Pred>(dest,pred);
}

Живой пример на Ideone. (Я непосредственно использовал bool s, чтобы сделать код короче, не заботясь о выводе и тому подобное.)

3 голосов
/ 01 февраля 2012

Метод erase предназначен для приема итераторов к одному и тому же объекту-контейнеру.Вы пытаетесь передать итераторы в temp_list, чтобы использовать их для удаления элементов из активного, что недопустимо по уважительным причинам, поскольку метод удаления диапазона последовательности предназначен для указания диапазона в этой последовательности для удаления.Важно, чтобы итераторы были в этой последовательности, потому что в противном случае мы указываем диапазон значений для удаления, а не диапазон в том же контейнере, что является гораздо более дорогостоящей операцией.

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

С помощью списка дляНапример, это может быть так просто:

for (ActiveList::iterator it = active.begin(); it != active.end();)
{
    if (it->no_longer_active())
    {
        inactive.push_back(*it);
        it = active.erase(it);
    }
    else
        ++it;
}

Однако иногда вектор может превзойти эти решения, и, возможно, вам нужен вектор по другим причинам (например, для обеспечения непрерывной памяти).В этом случае std :: remove_if - ваш лучший выбор.

Пример:

bool not_active(const YourObjectType& obj);
active_list.erase(
    remove_if(active_list.begin(), active_list.end(), not_active), 
    active_list.end());

Более подробную информацию об этом можно найти в разделе «erase-remove idiom», и вы можетеНужно использовать объекты-предикаты в зависимости от того, какие внешние состояния требуются, чтобы определить, не является ли объект более активным.

2 голосов
/ 01 февраля 2012

Функция std::vector::erase требует, чтобы итераторы были итераторами в этом векторе, но вы передаете итераторы из temp_list.Вы не можете удалить элементы из контейнера, который находится в совершенно другом контейнере.

1 голос
/ 01 февраля 2012

Я хотел бы предложить, что это пример использования std::list. Вы можете объединить участников из одного списка в другой. Посмотрите на std :: list :: splice () для этого.

Вам нужен произвольный доступ? Если нет, то вам не нужно std::vector.

Обратите внимание, что со списком, когда вы склеиваете, ваши итераторы и ссылки на объекты в списке остаются действительными.

Если вы не против сделать реализацию "навязчивой", ваши объекты могут содержать свое собственное значение итератора, чтобы они знали, где они находятся. Затем, когда они меняют состояние, они могут автоматизировать свое собственное «перемещение» из одного списка в другой, и вам не нужно пересекать весь список для них. (Если вы хотите, чтобы эта развертка произошла позже, вы можете заставить их «зарегистрироваться» для последующего перемещения).

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

 //fwd iterator must be writable
template< typename FwdIterator, typename InputIterator, typename Pred >
FwdIterator copy_and_remove_if( FwdIterator inp, FwdIterator end, InputIterator outp, Pred pred )
{
    for( FwdIterator test = inp; test != end; ++test )
    {
        if( pred(*test) ) // insert
        {
            *outp = *test;
            ++outp;
        }
        else // keep
        {
           if( test != inp )
           { 
              *inp = *test;
           }
           ++inp;
        }
   }
   return inp;
}

Это немного похоже на std::remove_if, но скопирует удаляемые в альтернативную коллекцию. Вы бы вызвали его так (для вектора), где isInactive является допустимым предикатом, который указывает, что его следует переместить.

active.erase( copy_and_remove_if( active.begin(), active.end(), std::back_inserter(inactive), isInactive ), active.end() );
1 голос
/ 01 февраля 2012
active.erase(temp_list.begin(), temp_list.end());

Вы пытаетесь удалить элементы из одного списка, но вы используете итераторы для второго списка. Итераторы первого списка не такие, как во втором списке.

0 голосов
/ 01 февраля 2012

Итераторы, которые вы передаете erase(), должны указывать на сам vector;Утверждение говорит вам, что они этого не делают.Эта версия erase() предназначена для удаления диапазона из vector.

. Вам нужно самостоятельно выполнить итерацию по temp_list и вызвать active.erase() в результате разыменования итератора на каждом шаге.

...