有状态函子 & STL:未定义的行为
我正在关注这个 函数对象教程
复制粘贴如下:
我无法理解以下内容:
谓词应始终实现为无状态函数对象,以避免出现意外结果。无法保证算法内部复制谓词的频率。因此,将谓词实现为有状态函数对象可能会产生未执行的结果。
示例如下:
#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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这句话的含义应该从字面上理解。对于大多数 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 simplest way to enforce this upon yourself is to define
operator()
asconst
.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 forremove_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 ofpos
are valid (because they are to be retained). There are no requirements on what elements exist frompos
onward (see here). (Chapter 32 of "Effective STL" by Scott Meyers has a good explanation of whyremove_if
(and so on) behave like this).