我正在写一个或多或少像这样的程序:
#include <list>
list<MyClass> things;
class MyClass {
// some stuff
void remove() {
things.remove_if(THING_IS_ME);
}
};
我需要写什么而不是THING_IS_ME?
换句话说,我正在使用全局STL列表作为事物的集合。在某些时候,列表中的一个对象会识别出它是多余的,并希望a)将自己从列表中删除,并且b)使自己被破坏。
我该怎么做?
我已经写了大约15年的C ++了,这个页面有点困惑:http://www.cplusplus.com/reference/algorithm/remove_if/
这些谓词是什么? C ++现在有更高阶的函数吗?
答案 0 :(得分:5)
过去15年来,C ++的情况发生了巨大变化。 1994年7月,亚历山大·斯捷潘诺夫(Alexander Stepanov)提出的关于通用编程理念的图书馆的提议获得了ANSI / ISO委员会的最终批准。我们今天方便地称之为STL的库随后成为标准C ++库。 STL的故事与它背后的想法一样迷人,它绝对值得一读。
你发现的std::remove_if()
函数只是这种哲学的另一种反映,它成为了C ++现代身份的一部分。简而言之,这是一个通用函数,可以处理任何元素的容器(序列)和任何(表现为a)的条件。为此,您必须提供两个功能:
事实证明,在这种情况下,您想要的谓词是相等的谓词。并且因为基于相等性移除元素是如此常见的任务,所以标准还提供std::remove()
函数,其假设隐式等式谓词。当然,您必须确保元素可以比较:
bool operator==(const MyClass& a, const MyClass& b)
{
// return true if the two are equal, and false otherwise.
}
然后我们可以使用我们的谓词删除MyClass
类型的元素:
std::remove(things.begin(), things.end(), *this); // if *this == elem
回想一下,标准函数std::remove()
适用于任何容器,即使是那些尚未创建的容器。因为每种容器都有自己的删除元素的方法,所以如果不知道它所使用的容器的实现细节,这个函数就无法真正执行删除。因此,std::remove()
函数交换元素,使得“已删除”元素位于容器的末尾。然后,它返回一个迭代器,指向连续元素“removed”的第一个元素。
typedef std::list<MyClass>::iterator iter;
iter first_removed = std::remove(things.begin(), things.end(), *this);
最后,我们通过调用特定容器的删除功能来真正删除元素,该功能适用于列表中的单个位置或要移除的一系列连续元素:
things.erase(first_removed, things.end());
在一行中看到这种代码并不罕见:
things.erase(std::remove(things.begin(), things.end(), *this),
things.end());
这一切似乎都是压倒性的和复杂的,但它有一些优点。首先,标准库的这种设计支持动态编程。它还允许标准库提供具有非常纤薄的接口的容器,以及几种可用于许多不同类型容器的自由功能。它允许您快速创建容器并立即获得标准库的所有功能以使用它。或者,它允许您快速编写一个通用函数,该函数可以立即与所有标准容器一起使用 - 那些已经写好的容器和那些尚未写入的容器。
答案 1 :(得分:2)
(最初是一组评论,但在发现OP实际想要做的事情之后重写为答案。)
您确实意识到STL容器存储了您插入的内容的副本,对吗?这意味着MyClass
的实例更具可比性(例如,通过operator==
) - 您不能只是比较地址,因为它们总是不同。
如果拥有MyClass的副本毫无意义,那么a pointer container可能会更好。
话虽这么说,C ++语言默认使用复制语义。该语言要求您在代码中明确指定引用。我强烈建议您选择a good C++ book,否则将来会被这样的问题绊倒。
答案 2 :(得分:0)
首先,一个简单的手写循环:
for( list<MyClass>::iterator it = things.begin(); it != things.end(); /* */ ) {
if( qualifiesForDelete( *it ) ) {
it = things.erase( it );
}
else {
++it;
}
}
其次,使用remove_if
算法。作为算法的remove_if
与list
的成员函数相反,实际上不能删除任何元素,而是将要删除的元素移动到列表的末尾。随后必须调用erase
。这是一个非常重要的习语,erase-remove idiom,必须学习。
things.erase(
remove_if( things.begin(), things.end(), deletionPredicate ),
things.end()
);
其中deletionPredicate
是一个函数或函数对象,它接受一个类型的参数并返回bool
。返回true
的元素被视为已删除。
答案 3 :(得分:0)
remove_if
函数有三个参数:两个用于定义正在处理的范围的迭代器,以及一个谓词(返回bool
的测试函数)。 start iterator指向你想要处理的第一个元素;结束迭代器指向范围中最后一个元素之后的元素。
在page you linked to的示例中,谓词是代码行
bool IsOdd (int i) { return ((i%2)==1); }
为了让你的代码做你想做的事,你需要写这样的东西:
things.remove_if(things.begin(), things.end(), SomePredicateFunction);
并且您定义SomePredicateFunction
就像这样(用适当的测试替换true
):
bool SomePredicateFunction (MyClass c) { return true; }