传递额外的参数给remove_if

Pass additional arguments to remove_if

本文关键字:remove if 参数      更新时间:2023-10-16

我想使用remove_if函数从向量中删除元素,但将擦除限制为N个元素。

的例子:

// predicate function that determines if a value is an odd number.
bool IsOdd (int i) {
  if (we deleted more than deleteLimit)
    return false;
  return ((i%2)==1);
}

void otherFunc(){
  int deleteLimit = 10;
  // remove odd numbers:                       
  std::vector<int>::iterator newEnd =
  std::remove_if (myints.begin(), myints.end(), IsOdd (how to pass deleteLimit?) );
}

我需要IsOdd谓词存储它删除了多少元素以及我们想要删除多少元素。唯一的方法是使用全局变量?这样的:

int deleteLimit = 10;
int removedSoFar = 0;
bool IsOdd (int i) {
  if (deleteLimit < removedSoFar)
    return false;
  if (i%2==1) {
    removedSoFar++
    return true;
  }
  return false;
}
remove_if ...

告诉"到目前为止已经删除了多少元素"的状态应该在函数/算法调用之外定义。这是因为函数函数不应该有一个在被调用时被修改的状态(这将是未定义的行为)。

你应该在函子的构造函数中引用这个状态(计数器)(或者在lambda中通过引用捕获),这样你就可以访问和修改这个计数器。当复制此函子时,算法调用哪一个都无关紧要,因为它们现在都保持对相同状态的引用。

使用函子(c++ 03):

class IsOdd {
    int deleteLimit;
    int & deletedSoFar;
public:
    IsOdd(int deleteLimit, int & deletedSoFar) :
        deleteLimit(deleteLimit), deletedSoFar(deletedSoFar)
    {}
    bool operator()(int i) const {
        if (deletedSoFar < deleteLimit && i % 2) {
            ++deletedSoFar;
            return true;
        }
        return false;
    }
};
int deletedSoFar = 0;
int deleteLimit = 10;
std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar));

使用lambda (c++ 11):

int deletedSoFar = 0;
int deleteLimit = 10;
auto it = std::remove_if (myints.begin(), myints.end(), [deleteLimit,&deletedSoFar](int i){
    if (deletedSoFar < deleteLimit && i % 2) {
        ++deletedSoFar;
        return true;
    }
    return false;
});
myints.erase(it, myints.end());

除了创建自己的函子,还可以传递lambda表达式:

auto deleteLimit = 25;
auto removedSoFar = 0;
auto it = remove_if (myints.begin(), 
                     myints.end(), 
                     [deleteLimit, &removedSoFar](int i)->bool 
                     { 
                       if ( (deletedSoFar < deleteLimit) && (i % 2)) {
                          ++deletedSoFar;
                          return true;
                       }
                       return false;
                      } );
// really remove the elements from the container
myints.erase(it, myints.end());
然而,要注意状态函数和std库算法并不总是很好的混合。在这里,对lambda的调用可能有副作用,因此您无法保证序列中的哪些元素将被删除。

注意最后一次呼叫std::vector::erase。这是真正从容器中删除不需要的元素所必需的。参见erase remove习语

int initial_size = myints.size();
std::remove_if (myints.begin(), myints.end(), [myints.size(),limit,initial_size](int i)->bool{  
return ( ( initial_size-myints.size() )<limit ? i%2 : false) } );

使用functor,与operator ()结构,或使用一些绑定特性(std::bind/boost::bind)

bool isOdd(int current, int limit, int& count)
{
   if (count >= limit)
   {
      return false;
   }
   if (current % 2 == 1)
   {
      ++count;
      return true;
   }
   return false;
}
int count = 0, limit = 10;
vec.erase(std::remove_if(vec.begin(), vec.end(),
std::bind(&isOdd, _1, limit, std::ref(count)), vec.end());

和函子

struct IsOdd : public std::unary_function<int, bool>
{
public:
   IsOdd(int l) : count(0), limit(l)
   {
   }
   bool operator () (int i)
   {
      if (count >= limit)
      {
         return false;
      }
      if (current % 2 == 1)
      {
         ++count;
         return true;
      }
      return false;
private:
   int count;
   int limit;
};
int limit = 10;
vec.erase(std::remove_if(vec.begin(), vec.end(),
isOdd(limit)), vec.end());

投票结果中的代码有一个小错误。在operator关键字前缺少圆括号。我无法编辑,因为它少于六个字符,我无法评论,因为我的分数太低。

class IsOdd {
        int deleteLimit;
        int & deletedSoFar;
    public:
        IsOdd(int deleteLimit, int & deletedSoFar) :
            deleteLimit(deleteLimit), deletedSoFar(deletedSoFar)
        {}
        bool operator()(int i) const {
            if (deletedSoFar < deleteLimit && i % 2) {
                ++deletedSoFar;
                return true;
            }
            return false;
        }
    };
    int deletedSoFar = 0;
    int deleteLimit = 10;
    std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar));