2015-12-24 1 views
3

В этом question показано, как использовать erase/remove_if на основе векторных индексов с использованием предиката функции. Это хорошо работает при первом вызове функции, но поскольку локальная статическая переменная поддерживает состояние, при следующем вызове другого вектора мне будет не повезло. Поэтому я думал, что могу использовать функтор с частной переменной, которая была бы повторно использована. В основном это работает, за исключением первого элемента. Там что-то специфичные для пути 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, используя его как временный объект или что-то, но я не совсем понимаю, что это значит.

EDIT: было бы здорово, если бы я мог избежать C++ 11

ответ

5

Да, реализация разрешена для копирования функции, поэтому вы можете получить запутанное поведение, если функционал имеет изменяемое состояние. Есть несколько способов обойти это. Самый простой, вероятно, использовать 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()); 
+0

Спасибо, это прекрасно работает с C++ 11. Есть ли решение для C++ 03? – confusedCoder

+0

@confusedCoder: Я добавил пример. –

+0

@confusedCoder: вы также можете легко сделать свой собственный 'reference_wrapper'. –

4

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

Чтобы преодолеть это, вы должны использовать ссылки на состояние внутри ваших функторов (в вашем случае укажите свою ссылку на 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; 
    } 

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

Смежные вопросы