Подтвердить что ты не робот

Std:: remove_if - lambda, не удаляя ничего из коллекции

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

    // If the device doesn't have any modes, remove it.

  std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
   [](DisplayDevice3d& device) 
   { 
    return device.Modes.size() == 0; 
   }
  ); 

Несмотря на то, что из 6 DisplayMode3d в MyDisplayDevices только 1 имеет какой-либо DisplayMode3d в своей коллекции Modes, ничего не удаляется из списка.

Какую ошибку числа я сделал здесь?

Edit:

А, хорошо, моя ошибка заключалась в том, что я должен использовать MyDisplayDevices.remove_if вместо std:: remove_if, однако приведенные ниже ответы верны для использования std:: remove_if: p.

MyDisplayDevices.remove_if( [](DisplayDevice3d const & device) 
                            { 
                                return device.Modes.size() == 0; 
                            });
4b9b3361

Ответ 1

Вам нужно вызвать erase на итераторе, возвращенном из remove_if, он должен выглядеть примерно так:

auto new_end = std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
                              [](const DisplayDevice3d& device)
                              { return device.Modes.size() == 0; });

MyDisplayDevices.erase(new_end, MyDisplayDevices.end());

Ответ 2

remove_if не удаляет что-либо из списка, он просто перемещает их до конца. Вам нужно использовать его вместе с erase. Подробнее см. question.

Ответ 3

Как уже упоминалось, есть способы заставить его работать. Однако мой совет состоял бы в том, чтобы полностью избежать remove_if и придерживаться стандартного удаления на основе итератора. Идиома ниже работает как для list, так и vector и не вызывает неожиданного поведения.

for( vector<TYPE>::iterator iter = vec.begin() ; iter != vec.end() ; )
  if( iter->shouldRemove )
    iter = vec.erase( iter ) ; // advances iter
  else
    ++iter ; // don't remove

Как упоминается ниже, этот метод имеет более высокую стоимость, чем remove_if, когда удалено более 1 элемента.

remove_if работает, копируя элементы дальше вперед в векторе и переписывая векторы, которые должны быть удалены из вектора тем, который находится непосредственно перед ним. Например: remove_if вызывается для вектора для удаления всех 0 элементов:

0 1 1 0 1 0

приводит к:

1 1 1 0 1 0

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

Пример ниже

#include <stdio.h>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void print( vector<int> &v )
{
  for( int i : v )
    printf( "%d ", i );
  puts("");
}

int main()
{
  vector<int> v = { 0, 1, 1, 0, 1, 0 };
  print( v ); // 0 1 1 0 1 0
  vector<int>::iterator it = remove_if( v.begin(), v.end(), [](int i){ return i == 0; } );
  print( v ); // 1 1 1 0 1 0
  v.erase( it, v.end() ); // actually cut out values not wanted in vector
  print( v ); // 1 1 1 (correct)
}

Ответ 4

remove_if не выполняет изменение размера, но вместо этого он просто возвращает итератор элементу, который следует за последним не удаленным элементом. Этот итератор можно передать в erase() для очистки.

введите описание изображения здесь