remove_if 基于带有函子的向量索引
remove_if based on vector index with a functor
此 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
就好了
一般来说,具有状态的仿函数对于 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,然后从找到的位置继续使用原始谓词 - 但原始谓词对新位置一无所知,反映在副本中。
是的,允许实现复制函数,因此如果函数具有可变状态,您可能会得到令人困惑的行为。有几种方法可以解决这个问题。最简单的可能是使用 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() );
此 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
就好了一般来说,具有状态的仿函数对于 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,然后从找到的位置继续使用原始谓词 - 但原始谓词对新位置一无所知,反映在副本中。
是的,允许实现复制函数,因此如果函数具有可变状态,您可能会得到令人困惑的行为。有几种方法可以解决这个问题。最简单的可能是使用 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() );