是标准库算法允许复制谓词参数呢?(Are Standard Library algorithms a

2019-09-18 05:03发布

假设我们想从向量中删除重复值int秒。 通常的解决方法是排序的矢量和擦除与擦除remove惯用法重复。 但是,我们需要十个分量不会被删除的元素的顺序,所以我们不能排序。 所以有人会想出这样的断言,并与使用remove_if算法:

struct comp {
    std::set<int> s;
    comp() : s() {}
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};

但是,这将打破,如果谓词对象将被复制出于某种原因,因为我们将得到的两个副本set成员。 事实上,海湾合作委员会实现remove_if正是这么做的:

template<typename _ForwardIterator, typename _Predicate>
    _ForwardIterator
    remove_if(_ForwardIterator __first, _ForwardIterator __last,
          _Predicate __pred)
    {

      __first = _GLIBCXX_STD_A::find_if(__first, __last, __pred);

      if(__first == __last)                             // ^^^^^ here a copy is made
        return __first;
      _ForwardIterator __result = __first;
      ++__first;
      for(; __first != __last; ++__first)
        if(!bool(__pred(*__first)))
          {
            *__result = _GLIBCXX_MOVE(*__first);
            ++__result;
          }
      return __result;
    }

解决方法是使set我们的仿函数的静态成员:

struct comp {
    static set<int> s;
    comp() { s. clear(); }
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};
set<int> comp::s;

但问题依然存在:

我们需要确保谓词函子的可能复制不会打破我们的逻辑是什么? 有没有在强制要求(或禁止)某些行为方面对这一问题的标准什么? 或者是在执行中的错误?

Answer 1:

是的,标准没有规定多少次谓语将被复制,也不在什么样的顺序谓词将应用于容器的内容说。 从本质上讲,谓语必须像纯函数 ; 他们必须有没有观察到的状态。 1

所以remove_if听起来不像一个合适的算法在这里。 黑客如存储set外部的函子将不会解决问题; 你仍然可以调用未定义的行为。


1.对于一个更深入的讨论,请参见第39项(“ 使谓词纯函数 ”)斯科特迈尔斯的有效STL



Answer 2:

我们需要确保谓词函子的可能复制不会打破我们的逻辑是什么?

是的,你应该承担的谓词被复制。 在C ++ 11,你可以考虑使用的std ::裁判或std :: CREF 。

另一种方法是修改comp结构采取set的参考:

struct comp {
    std::set<int>& s;
    comp(std::set<int> s) : s(s) {}
    bool operator()(int i)
    {
        return !(s.insert(i)).second;
    }
};

:我不是在上这是否会一起工作的任何声明remove_if ,我只是处理含状态复制谓词不应被复制的问题。

编辑正如在评论中指出,该办法是从根本上打破了。 谓词调用的结果不应该依赖于可变状态。



Answer 3:

是的,他们被允许复制的参数的时间不确定的数量。 不是使成员组静态更好的方法是将仿函数外部创建的集并把它传递一个构造函数的参数。 在内部存储的指针。



文章来源: Are Standard Library algorithms allowed to copy predicate arguments?