从 C++ 中的非原始对象向量中删除所有匹配元素

Remove all matching elements from vector of non-primitive objects in C++

假设我有一个 class:

class sampleVector
{
  public:
  int a;
  int b;
  string c;
}

现在我有一个包含多个 sampleVector 对象的向量,但是该向量有 2(multiple) consecutive 个 sampleVector 对象,它们具有相同的a(比如 5)和 b(比如 10)的值。

现在我想从具有 a=5 和 b=10 的向量中删除所有 sampleVector 对象。

问题是,对于多次连续出现,下面是一种方法:

for (;it!=itEnd;it++)
{
    if (it->getA() == 5 && it->getB() == 10)
    {
        vec.erase(it);
        it=vec.begin(); // Resetting this is must
    }
}

但我想知道如何为此使用 "remove",因为以下内容不起作用:

for (;it!=itEnd;it++)
        {
                if (it->getA() == 5 && it->getB() == 10)
                {
                        vec2.erase(remove(vec2.begin(), vec2.end(), *it), vec2.end()); // doesn't even compile
                }
        }

当我们有一个原始数据类型的向量并且我们有一个需要删除的特定值时,我们可以使用这种删除方式。但是对于非原始数据类型的向量,我们如何通过传递迭代器而不是值来使用 "remove"?

您不需要为此使用循环,因为 std::remove documentation 状态;

Removes all elements satisfying specific criteria from the range [first, last)

如果您想删除仅与 class 的一部分匹配的特定项目,您应该使用 std::remove_if 并提供谓词:

template< class ForwardIt, class UnaryPredicate > ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );

例如:

std::remove_if(std::begin(vec), std::end(vec),
            [](sampleVector& v) { return (v.a == 5 && v.b==10); });

然后您可以像以前一样将其传递给 std::erase

您可以通过两种方式实现这一点:向您的 class 添加一个比较运算符,或者将 std::remove_if 与一个进行比较的函数一起使用。

添加比较运算符

在大多数情况下,如果您可以访问 class sampleVector,则首选此方法。这样,您将运算符与 class 一起分发,因此其他用户不必为此编写函数。由于元素是 public,您也可以将运算符作为非成员函数,但非成员函数将无法访问私有字段。

class sampleVector
{
  public:
  int a;
  int b;
  string c;
  bool operator==(const sampleVector& other);
}

bool sampleVector::operator==(const sampleVector& other)
{
  return a == other.a && b == other.b;
}

//somewhere else
sampleVector elementToRemove {5, 10, ""}; //or however you find the element you want to remove
std::erase(std::remove(vec2.begin(), vec2.end(), elementToRemove), vec2.end());

使用std::remove_if

如果您不能修改 sampleVector 的内容或者比较 class 有多种可能性(例如这里您只检查 ab,但在其他情况下您也想比较 c)。如果你想比较私人成员(除非你为他们提供吸气剂),它不会工作。

std::erase(std::remove_if(vec2.begin(), vec2.end(), [](const sampleVector& v) {
                                                    return v.a == 5 && v.b == 10;}), vec2.end());

实际上std::remove算法不会删除矢量元素,而是在最后移动它们。 对于您的情况,您可以使用下一种方法:

auto it = std::remove_if(vec.begin(), vec.end(), [](auto&& item)
{ return (item.a == 5 && item.b == 10); });
vec.erase(it, vec.end());

备注: std::remove_if 的第一个和第二个参数是迭代器,分别指示向量的开始和结束。 第三个参数 - 是可调用对象(在我们的例子中是 lambda),即 return true - 如果当前对象需要删除,否则为 false。 Return 该算法的值 - 是第一次删除对象时的迭代器。

std::remove_if and vector.erase

的例子
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

struct SampleVector
{
  int a;
  int b;
  std::string c;
};

std::ostream& operator<<(std::ostream& to, const SampleVector & v)
{
    return to << "{ a=" << v.a << ", b=" << v.b << ", c=" << v.c << " }";
}


int main(int argc, char const *argv[]) {

   std::vector< SampleVector > data = { {5,10,"5-10"}, {3,12,"3-12"}, {5,10,"5-10"} };

   std::cout << "Before [";
   std::for_each(data.begin(), data.end(), [] (const SampleVector& v) {
            std::cout << ' ' << v << ' ';
    } );
   std::cout << ']' << std::endl;

   data.erase( std::remove_if(data.begin(), data.end(), [] (const SampleVector& v)
    { return 5 == v.a && 10 == v.b; } ), data.end() );


   std::cout << "After [";
   std::for_each(data.begin(), data.end(), [] (const SampleVector& v) {
            std::cout << ' ' << v << ' ';
    } );
   std::cout << ']' << std::endl;

    return 0;
}