Доступ к итератору for_each из лямбда

Можно ли получить доступ к итератору std::for_each, чтобы я мог стереть текущий элемент из std::list с помощью лямбда (как показано ниже)

typedef std::shared_ptr<IEvent>    EventPtr;
std::list<EventPtr> EventQueue;
EventType evt;
...

std::for_each( 
    EventQueue.begin(), EventQueue.end(),

    [&]( EventPtr pEvent )
    {
        if( pEvent->EventType() == evt.EventType() )
            EventQueue.erase( ???Iterator??? );
    }
);

Я читал об использовании [](typename T::value_type x){ delete x; } здесь, на SO, но VS2010, похоже, не нравится это утверждение (подчеркивает T как источник ошибки).


person fishfood    schedule 28.06.2012    source источник


Ответы (3)


Вы используете неправильный алгоритм. Используйте 1_:

EventQueue.remove_if([&](EventPtr const& pEvent)
{
    return pEvent->EventType() == evt.EventType();
});

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

(Кроме того, подумайте, действительно ли вы хотите использовать std::list; маловероятно, что это правильный контейнер для вашего варианта использования. Подумайте о std::vector, с которым вы будете использовать стереть/удалить идиому, чтобы удалить элементы, удовлетворяющие определенному предикату.)

person James McNellis    schedule 28.06.2012
comment
Я начал с std::queue‹EventPtr›, но перешел на список, потому что мне нужно выполнить итерацию всей очереди для некоторых функций. Кроме этого, я думал, что список будет более эффективным, чем вектор, который может увеличиваться/уменьшаться со временем. - person fishfood; 28.06.2012
comment
std::list реализован как двусвязный список; он редко имеет лучшие характеристики производительности, чем std::vector, реализованный с использованием массива. Время, затрачиваемое на перемещение объектов в массиве (особенно небольших объектов, таких как shared_ptr объектов), намного меньше, чем время, необходимое для обхода узлов связанного списка и динамического выделения и уничтожения узлов. std::vector должен быть выбором по умолчанию для контейнера последовательности; переключайтесь на другой только в том случае, если производительность является проблемой, а профилирование показывает, что другой контейнер будет работать лучше. - person James McNellis; 28.06.2012
comment
Разве вектор не плох для вставки данных посередине? Я думал, что эффективная вставка была преимуществом использования связанных списков. - person fishfood; 28.06.2012
comment
Это зависит, но обычно нет. Поиск местоположения, в которое необходимо вставить данные, намного дороже для list, потому что связи узлов должны проходиться одна за другой, пока не будет найдена правильная позиция. Каждая ссылка является указателем, который требует косвенного обращения к некоторому произвольному месту в памяти. В vector элементы хранятся непрерывно, что означает, что это намного, намного удобнее для современных функций ЦП (кеши, предварительная выборка). Есть несколько крайних случаев, когда list мог бы работать лучше, но их мало и они очень, очень далеки друг от друга. - person James McNellis; 28.06.2012

нет, используйте вместо этого обычный for.

for( auto it = EventQueue.begin(); it != EventQueue.end(); ++it )
{
  auto pEvent = *it;
  if( pEvent->EventType() == evt.EventType() )
      it = EventQueue.erase( it );
);
person smerlin    schedule 28.06.2012
comment
Я немного разочарован этим - это снижает полезность лямбда-выражений, спасибо! - person fishfood; 28.06.2012
comment
@lapin: нет, но вы пытались использовать std::for_each для чего-то, для чего он не предназначен. Если вы хотите что-то удалить из контейнера, std::for_each — не тот инструмент. Вместо этого используйте стирание + std::remove_if. И std::remove_if, конечно же, работает и с лямбда-выражениями... - person smerlin; 28.06.2012

Стирание — не единственный случай, когда вам может понадобиться узнать итератор от лямбда. Чтобы сделать это более общим способом, я использую оператор & (неявное преобразование в итератор) следующим образом:

int main (int argc, char* argv []) {
  size_t tmp [6] = {0, 1, 2, 3, 4, 5};
  std::list<size_t> ls ((size_t*)tmp, (size_t*) &tmp [6]);
  //printing next element
  std::for_each ((const size_t*)tmp, (const size_t*) &tmp [5], [] (const size_t& s) {
    std::cout << s << "->";
    std::cout << *(&s +1) << "   ";
  });
  std::cout << std::endl;
}
person Saint-Martin    schedule 13.02.2019