我正在关注此Function objects tutorial
下面的复制意大利面:
我无法理解以下内容:
谓词应始终作为无状态函数对象实现,以避免意外结果。无法保证算法在内部复制谓词的频率。因此,具有作为有状态函数对象实现的谓词可能具有未执行的结果。
示例如下:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
class predicate
{
public:
predicate(int condition) :
condition_(condition), state_(0) {}
bool operator()(int) { return ++state_ == condition_; }
private:
int condition_;
int state_;
};
int main()
{
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
predicate p(2);
std::vector<int>::iterator pos =
std::remove_if(vec.begin(), vec.end(), p);
vec.erase(pos, v.end());
std::copy(vec.begin(), vec.end(),
std::ostream_iterator<int>(std::cout, " "));
return 0;
}
如果我理解(读)它正确,它试图删除向量中标记为2的元素。 remove_if算法返回容器的新端并尝试擦除所有容器。
输出:
1 3 5
显然,不仅第二个元素被删除,第四个元素也被删除。这种好奇心的答案很简单,所使用的算法'remove_if'在执行期间在内部复制谓词。此内部副本创建一个包含其原始状态的新谓词对象。
虽然我可以阅读似乎正在发生的事情,但我无法想象幕后发生的事情,即使是第四个要移动到容器末端的元素。这与单程或多程算法有关吗? (如果有人能指出我如何推断出同样的方法,我将不胜感激)
另一方面,如果我评论擦除&amp;注意输出。
1 3 5 4 5
导致容器损坏的原因是什么?
答案 0 :(得分:20)
该引用的含义应以面值表示。对于大多数STL算法,您不应该实现谓词仿函数,使其具有可观察状态(AKA“副作用”),因为:
最简单的方法是将operator()
定义为const
。
有一些例外情况,例如for_each
,上述情况均不适用。您可以在这里使用有状态仿函数。有关详细信息,请参阅此优秀文章:http://drdobbs.com/cpp/184403769。
在幕后,您的STL实现的作者可以随心所欲地编写remove_if
(和其他算法),只要它符合标准规定的要求即可。没有真正的理由过分担心你为什么会得到你所看到的行为,除了承认它是未定义的。如果你想知道具体细节,我只想看看你正在使用的STL实现中remove_if
的代码。
至于你的旁注;这不是“腐败”,它只是remove_if
如何工作的工件(即使对于有效的谓词也会发生这种情况)。唯一的要求是pos
的 left 的所有元素都是有效的(因为它们将被保留)。对于pos
之后存在哪些元素没有要求(请参阅here)。 (Scott "Effective STL"第32章对于remove_if
(等等)的行为有很好的解释。