remove_if на основе векторного индекса с функтором

это вопрос показал, как использовать erase / remove_if на основе векторных индексов с использованием предиката функции. Это работает хорошо при первом вызове функции, но поскольку локальная статическая переменная поддерживает состояние, при следующем вызове другого вектора мне не повезет. Поэтому я подумал, что мог бы использовать функтор с закрытой переменной, которую можно использовать повторно. Это в основном работает за исключением первого элемента. Есть что-то особенное в том, как remove_if использует функтор, который портит инициализацию приватной переменной

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

class is_IndexEven_Functor {
public:
is_IndexEven_Functor() : k(0) {}

bool operator()(const int &i) {
cout << "DEBUG: isIndexEvenFunctor: k " << k << "\ti " << i << endl; ////

if(k++ % 2 == 0) {
return true;
} else {
return false;
}
}
private:
int k;
};

int main() {

is_IndexEven_Functor a;
a(0);
a(1);
a(2);
a(3);

vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);

cout << "\nBefore\n";
copy(v.begin(), v.end(), ostream_iterator<int>(cout, " ")); cout << endl;

is_IndexEven_Functor b;
v.erase( remove_if(v.begin(), v.end(), b), v.end() );

cout << "\nAfter\n";
copy(v.begin(), v.end(), ostream_iterator<int>(cout, " ")); cout << endl;

return 0;
}

Вот вывод:

DEBUG: isIndexEvenFunctor: k 0  i 0
DEBUG: isIndexEvenFunctor: k 1  i 1
DEBUG: isIndexEvenFunctor: k 2  i 2
DEBUG: isIndexEvenFunctor: k 3  i 3

Before
0 1 2 3

DEBUG: isIndexEvenFunctor: k 0  i 0
DEBUG: isIndexEvenFunctor: k 0  i 1  // why is k == 0 here ???
DEBUG: isIndexEvenFunctor: k 1  i 2
DEBUG: isIndexEvenFunctor: k 2  i 3

After
2

Суть вопроса в том, почему ценность k равен 0 при втором вызове функтора (и как мне это исправить)?
Я предполагаю, что это как-то связано с использованием remove_if как временного объекта или чего-то еще, но я не совсем понимаю, что это значит.

РЕДАКТИРОВАТЬ: было бы здорово, если бы я мог избежать c ++ 11

3

Решение

Да, реализации разрешено копировать функцию, поэтому вы можете запутаться, если функционал имеет изменяемое состояние. Есть несколько способов обойти это. Самый простой, вероятно, использовать std::reference_wrapper, который может быть создан с помощью std::ref функция:

is_IndexEven_Functor b;
v.erase( remove_if(v.begin(), v.end(), std::ref(b)), v.end() );

Теперь вместо создания объекта функции реализация копирует оболочку, поэтому существует только один экземпляр исходного объекта функции.

Другой вариант — сохранить индекс отдельно от функции:

class is_IndexEven_Functor {
public:
is_IndexEven_Functor(int &index) : k(index) {}

.
.
.
private:
int &k;
};

и используйте это так:

int index = 0;
is_IndexEven_Functor b(index);
v.erase( remove_if(v.begin(), v.end(), b), v.end() );
6

Другие решения

Как правило, функторы с состоянием не так хороши для алгоритмов STL, так как они ничего не гарантируют в обработке функтора. Несмотря ни на что, он может создавать новую копию функтора (из оригинала!) Для каждой итерации для выполнения проверок. STL предполагает, что функторы не имеют состояния.

Чтобы преодолеть это, вы должны использовать ссылки для определения состояния внутри ваших функторов (в вашем случае, у вашей k должна быть ссылка на int, которая инициализируется раньше) или передавать сами функторы в оболочках ссылок. Чтобы ответить на конкретный вопрос (то есть, что происходит в remove_if), давайте посмотрим на реализацию (одна из реализаций, конечно):

 __remove_if(_ForwardIterator __first, _ForwardIterator __last,
_Predicate __pred)
{
__first = std::__find_if(__first, __last, __pred);
if (__first == __last)
return __first;
_ForwardIterator __result = __first;
++__first;
for (; __first != __last; ++__first)
if (!__pred(__first))
{
*__result = _GLIBCXX_MOVE(*__first);
++__result;
}
return __result;
}

Как видите, он использует КОПИЮ предиката для find_if и затем продолжает исходный предикат из найденной позиции — но оригинал ничего не знает о новой позиции, отраженной в копии.

4

По вопросам рекламы [email protected]