有状态函子和STL:未定义的行为

Stateful functors & STL : Undefined behaviour

本文关键字:未定义 STL 状态      更新时间:2023-10-16

我正在学习函数对象教程

复制下面的意大利面:

我无法理解以下内容:

谓词应始终作为无状态函数对象实现,以避免意外结果。无法保证算法内部复制谓词的频率。因此,将谓词实现为有状态函数对象可能会产生未被发现的结果

示例如下:

#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"在执行谓词时会在内部复制谓词。这个内部副本创建了一个包含其原始状态的新谓词对象

虽然我能读到似乎正在发生的事情,但我无法想象幕后发生的事情——即使是第四个元素也被移到了容器的末端。这与算法是单程还是多次有关?(如果有人能给我指明正确的方向,我也会很感激)

在旁注上,如果我评论擦除&注意输出。

1 3 5 4 5

是什么原因导致容器损坏?

该引号的含义应以面值表示。对于大多数STL算法,您不应该实现谓词函子,使其具有可观察状态(也称为"副作用"),因为:

  • 容器上的迭代顺序没有被定义
  • 该算法可以自由地复制函子
  • 为了不损坏容器内容或崩溃,算法可能依赖无状态

operator()定义为const是对自己强制执行此操作的最简单方法。

也有例外情况,如for_each,上述情况均不适用。您可以在这里自由使用有状态函子。有关更多信息,请参阅这篇精彩的文章:http://drdobbs.com/cpp/184403769.

在幕后,STL实现的作者可以随心所欲地编写remove_if(和其他算法),只要它符合标准规定的要求。没有真正的理由过于担心你看到的行为的确切原因,除了承认它是不明确的。如果您想了解具体情况,我只想看看您正在使用的STL实现中remove_if的代码。

至于你的旁注;这不是"损坏",它只是remove_if工作方式的产物(即使对于有效的谓词也会发生这种情况)。唯一的要求是pos左侧的所有元素都是有效的(因为它们将被保留)。对于pos以后存在的元素没有任何要求(请参见此处)。(Scott Meyers的"有效STL"第32章很好地解释了remove_if(等等)为什么会这样做)。