这个question展示了如何使用函数谓词基于向量索引使用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
那就太棒了答案 0 :(得分:6)
是的,允许实现复制该函数,因此如果函数具有可变状态,则可能会出现混乱行为。有几种方法可以解决这个问题。最简单的可能是使用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() );
答案 1 :(得分:4)
通常,具有状态的仿函数对于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的谓词的COPY,而不是继续使用找到位置的原始谓词 - 但是原始对新位置一无所知,反映在副本中。