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

发布于 2024-11-09 11:06:43 字数 1456 浏览 2 评论 0原文

我正在关注这个 函数对象教程

复制粘贴如下:

我无法理解以下内容:

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

示例如下:

#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

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

I am following this Function objects tutorial

Copy-pasta below:

I am unable to understand the following:

Predicates should always be implemented as stateless function objects to avoid unexpected results. There is no guarantee how often an algorithm might internally copy the predicate. Thus, having predicates that are implemented as stateful function objects might have unexecpted results.

The example is as follows:

#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;
}

If I understand (read) it correctly , it is attempting to remove the element marked as 2 in the vector. remove_if algorithm returns a new end of the container and attempting to erase all of it.

Output :

1 3 5

Clearly, not only the second element has been removed but also the fourth one. The answer to this curiosity is simply that the used algorithm 'remove_if' internally copies the predicate during its execution. And this internal copy creates a new predicate object containing its original state.

Though I can read what seems to be happening, I am unable to picture what is happening behind the scenes which actually marked even the 4th element to be moved to the end of the container. Does this have to do with an algorithm being single pass or multiple pass? ( also I would be grateful if some one could point me in the right direction how to deduce the same)

On a side note , if i comment the erase & note the output.

1 3 5 4 5

What causes the container to get corrupted ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

日久见人心 2024-11-16 11:06:43

这句话的含义应该从字面上理解。对于大多数 STL 算法,您不应该实现谓词函子,使其具有可观察状态(也称为“副作用”),因为:

  • 容器上的迭代顺序未定义,
  • 算法可以自由地复制函子,
  • 算法可能依赖无状态性,以免损坏容器内容或崩溃。

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

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

在幕后,STL 实现的作者可以以任何他们喜欢的方式自由编写 remove_if (和其他算法),只要它符合标准规定的要求即可。除了承认它是未定义的之外,没有真正的理由过多担心您所看到的行为的确切原因。如果您想了解具体细节,我只需查看您正在使用的 STL 实现中的 remove_if 代码即可。

至于你的旁注;这不是“损坏”,它只是 remove_if 工作方式的产物(即使对于有效的谓词也会发生这种情况)。唯一的要求是 pos左边的所有元素都有效(因为它们要被保留)。对于从 pos 开始存在哪些元素没有任何要求(请参阅此处)。 ("有效 STL" 很好地解释了为什么 remove_if (等等)的行为如此)。

The meaning of that quote should be taken at face value. For the majority of the STL algorithms, you should not implement your predicate functor such that it has observable state (AKA "side effects"), because:

  • the iteration order over the container is not defined,
  • the algorithm is free to make copies of the functor,
  • the algorithm may depend on statelessness in order to not corrupt the container contents or crash.

The simplest way to enforce this upon yourself is to define operator() as const.

There are exceptions, such as for_each, for which none of the above apply. You are free to use stateful functors here. For more info, see this excellent article: http://drdobbs.com/cpp/184403769.

Behind the scenes, the authors of your STL implementation are free to write the remove_if (and other algorithms) any way they like, so long as it conforms to the requirements laid down by the standard. There's no real reason to worry too much about exactly why you're getting the behaviour you're seeing, beyond acknowledging that it's undefined. If you want to know the specifics, I would just take a look at the code for remove_if in the STL implementation that you're using.

As for your side-note; this is not "corruption", it's simply an artifact of how remove_if works (this would occur even for a valid predicate). The only requirement is that all the elements to the left of pos are valid (because they are to be retained). There are no requirements on what elements exist from pos onward (see here). (Chapter 32 of "Effective STL" by Scott Meyers has a good explanation of why remove_if (and so on) behave like this).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文