我想从vector
中移除一些元素并使用remove_if
算法来执行此操作。但我想跟踪已删除的元素,以便稍后我可以对它们执行某些操作。我用以下代码尝试了这个:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
struct IsEven
{
bool operator()(int n)
{
if(n % 2 == 0)
{
evens.push_back(n);
return true;
}
return false;
}
vector<int> evens;
};
int main(int argc, char **argv)
{
vector<int> v;
for(int i = 0; i < 10; ++i)
{
v.push_back(i);
}
IsEven f;
vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), f);
for(vector<int>::iterator it = f.evens.begin(); it != f.evens.end(); ++it)
{
cout<<*it<<"\n";
}
v.erase(newEnd, v.end());
return 0;
}
但是这不起作用,因为remove_if
接受我的仿函数对象的副本,因此无法访问存储的evens
向量。实现这一目标的正确方法是什么?
P.S。 :例如,偶数和赔率仅仅是为了清酒,我的真实代码是不同的。因此,不建议以不同方式识别偶数或赔率。
答案 0 :(得分:9)
解决方案不是remove_if
,而是堂兄 partial_sort partition
。区别在于remove_if
仅保证[begin, middle)
包含匹配元素,但partition
也保证[middle, end)
包含与谓词不匹配的元素。
因此,您的示例变得公正(请注意,不再需要evens
):
vector<int>::iterator newEnd = partition(v.begin(), v.end(), f);
for(vector<int>::iterator it = newEnd; it != v.end(); ++it)
{
cout<<*it<<"\n";
}
v.erase(newEnd, v.end());
答案 1 :(得分:3)
你最好的选择是std::partition()
,它将重新排列序列中的所有elts,例如你的谓词返回true
将位于其返回false的所有elts之前。
例:
vector<int>::iterator bound = partition (v.begin(), v.end(), IsEven);
std::cout << "Even numbers:" << std::endl;
for (vector<int>::iterator it = v.begin(); it != bound; ++it)
std::cout << *it << " ";
std::cout << "Odd numbers:" << std::endl;
for (vector<int>::iterator it = bound; it != v.end(); ++it)
std::cout << *it << " ";
答案 2 :(得分:2)
如果您通过参考传递ist,则可以避免复制您的仿函数(即按值传递):
vector<int>::iterator newEnd = remove_if(v.begin(), v.end(),
boost::bind<int>(boost::ref(f), _1));
如果您无法使用boost
,则std::ref
也可以使用{{1}}。我测试了上面的代码,它按预期工作。
答案 3 :(得分:1)
额外的间接级别。在本地声明向量,和
让IsEven
包含一份副本。 IsEven
也可以
拥有该向量,前提是它是由动态分配和管理的
一个shared_ptr
。在实践中,我一般都找到了局部变量
加指针解决方案更方便。类似的东西:
class IsEven
{
std::vector<int>* myEliminated;
public:
IsEven( std::vector<int>* eliminated = NULL )
: myEliminated( eliminated )
{
}
bool
operator()( int n ) const
{
bool results = n % 2 == 0;
if ( results && myEliminated != NULL ) {
myEliminated->push_back( n );
}
return results;
}
}
请注意,这也允许operator()()
函数为const
。一世
认为这是正式要求的(虽然我不确定)。
答案 4 :(得分:0)
我在代码中看到的问题是,每次remove_if算法调用时,都会在struct中创建evens
向量。所以不管你是否将一个仿函数传递给remove_if它每次都会创建一个新的向量。因此,一旦删除了最后一个元素,当函数调用结束并从函数中退出时,f.evens将始终获取一个空向量。这可以用两种方式分类,
编辑:
正如nabulke所建议的,你也可以std :: ref喜欢这个,std :: ref(f)。这可以防止您将矢量设置为全局并避免不必要的静态。
使其全球化的样本如下,
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
vector<int> evens;
struct IsEven
{
bool operator()(int n)
{
if(n % 2 == 0)
{
evens.push_back(n);
return true;
}
return false;
}
};
int main(int argc, char **argv)
{
vector<int> v;
for(int i = 0; i < 10; ++i)
{
v.push_back(i);
}
IsEven f;
vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), f);
for(vector<int>::iterator it = evens.begin(); it != evens.end(); ++it)
{
cout<<*it<<"\n";
}
v.erase(newEnd, v.end());
return 0;
}
这段代码对我来说似乎很合适。如果这不是您想要的,请告诉我。
答案 5 :(得分:0)
您可能有其他解决方案;只有你不需要在同一时间删除elts(是吗?)。使用std::for_each()
返回您的仿函数的副本。例:
IsEven result = std::for_each(v.begin(), v.end(), IsEven());
// Display the even numbers.
std::copy(result.evens.begin(), result.evens.end(), std::ostream_iterator<int> (cout, "\n"));
请注意,尽可能在c ++中创建未命名的变量总是更好。在这里,该解决方案并不能完全解决您的主要问题(从源容器中删除elts),但它会提醒所有人std :: for_each()返回您的仿函数的副本。 : - )