C++ 是否允许标准库算法复制谓词参数?

C++ 是否允许标准库算法复制谓词参数?,c++,g++,functor,stl-algorithm,C++,G++,Functor,Stl Algorithm,假设我们想从ints的向量中删除重复值。通常的解决方案是对向量进行排序,并使用erase-remove习惯用法删除重复项。但是我们需要保持不被删除元素的顺序,所以我们不能排序。因此,我们可能会想出这样一个谓词,并与remove\u一起使用,如果算法: struct comp { std::set<int> s; comp() : s() {} bool operator()(int i) { return !(s.insert(i)).

假设我们想从
int
s的向量中删除重复值。通常的解决方案是对向量进行排序,并使用erase-remove习惯用法删除重复项。但是我们需要保持不被删除元素的顺序,所以我们不能排序。因此,我们可能会想出这样一个谓词,并与
remove\u一起使用,如果
算法:

struct comp {
    std::set<int> s;
    comp() : s() {}
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};
解决方法是使
set
函子的成员为静态:

struct comp {
    static set<int> s;
    comp() { s. clear(); }
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};
set<int> comp::s;
struct comp{
静态集s;
comp(){s.clear();}
布尔运算符()(int i)
{
返回!(s.插入(i))。秒;
}
};
设置comp::s;
但问题仍然是:

我们需要确保谓词函子的可能副本不会破坏我们的逻辑吗?
标准中是否有任何规定(或禁止)与此问题相关的特定行为?或者这是实现中的一个bug?

是的,他们可以无限次地复制参数。比使成员集为静态更好的方法是在函子外部创建该集,并将其作为构造函数参数传递。在内部存储指针

我们是否需要确保谓词函子的可能副本不会破坏我们的逻辑

是的,您应该假设谓词是复制的。在C++ 11中,可以考虑使用. 另一种方法是修改您的
comp
结构,通过引用获取
集合

struct comp{
std::set&s;
comp(std::set s):s(s){
布尔运算符()(int i)
{
返回!(s.插入(i))。秒;
}
};
注意:我不是在说明这是否适用于
remove\u。如果
,我只是在解决复制的谓词包含不应复制的状态的问题


编辑正如评论中指出的那样,这种方法从根本上被打破了。谓词调用的结果不应取决于可变状态。

是的,标准没有指定谓词将被复制多少次,也没有说明谓词将以什么顺序应用于容器的元素。从本质上讲,谓词的行为必须类似于;它们必须没有可观察的状态。1

因此,如果这里的
听起来不是一个合适的算法,那么
删除\u。诸如将
集合
存储到函子外部之类的破解方法并不能解决问题;您仍将调用未定义的行为



1.有关更深入的讨论,请参见Scott Meyers的第39项(“使谓词成为纯函数”)。

我认为这一结果仍然没有得到很好的定义;该标准没有规定
remove\u if
必须将谓词应用于容器元素的顺序。@OliCharlesworth:true,但是由于该标准规定它适用于前向迭代器,出于效率原因,大多数实现实际上会按顺序应用它。有时,我想知道是否应该在标准中明确要求这一点,标准的问题在于,人们通常提出的是,人们最终依赖(往往没有意识到)那些依赖于实现的保证。@OliCharlesworth:除了Matthieu已经提到的,更重要的是,结果存储在
输出迭代器中(即它不能回溯),谓词的执行次数正好是容器的大小。除了前向循环测试每个元素并决定是否复制之外,不可能以任何其他方式实现;该标准没有规定
remove\u if
必须将谓词应用于容器元素的顺序。@OliCharlesworth我甚至没有考虑
remove\u if
,只是处理复制的谓词问题。我会澄清我的答案。这都是同一个问题的一部分。谓词的行为必须像它们是一样。@OliCharlesworth您完全正确。我甚至没有意识到谓词调用正在修改集合并基于该变异操作返回。@MatthieuM.:我认为
for_each
的关键在于它的参数(在标准中)被简单地称为
函数,与例如
remove\u if
不同,它被称为
谓词
。有一些算法由标准指定顺序。像
std::copy
,不是吗?而
备注是什么:在标准中稳定的
,这不是要求保留顺序吗?@jrok:它稳定的唯一意义是保留元素的相对顺序没有改变,而不是OP希望的。(并且
copy
不接受谓词;
copy\u如果
接受谓词,但没有指定应用的顺序。)与此同时,我在标准(25.1.10)中找到了关于复制谓词的相关部分,但似乎找不到任何关于不允许保留状态的谓词的内容(并不是说我不相信你或Scott Meyers——这本书肯定在我的待办事项列表上:)@jrok谓词可以保持可变状态。只是这可能会给您带来麻烦,特别是如果您使用该状态来确定在调用期间返回什么。谓词应该为相同的输入返回相同的值,但标准不会阻止您执行其他操作。虽然没有为
remove\u copy\u if
定义谓词调用顺序,但算法上的其他约束暗示了这一点:输出通过
OutputIterator
存储(只能递增,不能递减或随机访问),结果是稳定的(一个实现不能从末尾开始测试,因为它只能在一个前向过程中写入),最多执行N次谓词,这意味着算法必须在一个过程中执行。除非我遗漏了什么,否则pr
struct comp {
    static set<int> s;
    comp() { s. clear(); }
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};
set<int> comp::s;
struct comp {
    std::set<int>& s;
    comp(std::set<int> s) : s(s) {}
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};