remove_if比较同一向量中的两个元素

时间:2014-07-05 19:15:40

标签: c++ vector std

我有一个对象矢量。让我们打电话给他们" myObj"。我需要运行myObj的向量并确定当前对象是否与该向量中的另一个myObj交互,如果其中任何一个与另一个交互,则删除这两个对象。

for (std::vector<myObj*>::iterator it = objects.begin(); it < objects.end(); ++it)
{
    myObj*& r1 = (*it);
    myObj*& r2 = (*(it + 1));
    if ( r1->interactsWith(r2) )
    {
        objectInteracted(r1);
        objectInteracted(r2);
        delete r1;
        delete r2;
        r1 = NULL;
        r2 = NULL;
        ++it;
    }
}

objects.erase(std::remove(objects.begin(), objects.end(), NULL), objects.end()); //Causing "no conversion from const int to myObj"

正如您所看到的,我尝试使用移除擦除习惯用法从向量中清除这些对象。但是,在尝试编译时,我收到一个错误:&#34; C2446&#39; ==&#39;:没有来自&#39; const int&#39;到&#39; myObj *&#39;

我更喜欢使用remove_if函数,但由于我的谓词需要接受并比较同一向量中的两个元素,所以我不知道如何正确构造谓词。有人可以告诉我为什么我会收到编译错误,或者告诉我如何为上面的内容构造正确的谓词?我已经谷歌了几个小时,没有发现任何显示谓词的行为更复杂的例子,比如从列表中删除奇数,除了当前正在测试的元素之外,自然不需要任何输入。

3 个答案:

答案 0 :(得分:3)

问题是你的NULL作为整数传递给std::remove,而不是指针。如果使用C ++ 11是一个选项,请使用nullptr代替NULL(在for循环和std::remove内)。如果不是,请将NULL强制转换为适当的类型:

objects.erase(std::remove(objects.begin(), objects.end(), static_cast<myObj*>(NULL)), 
              objects.end()); //Causing "no conversion from const int to myObj"

答案 1 :(得分:3)

根据您的问题描述,听起来您需要一个谓词来测试给定对象是否与向量中的任何其他对象进行交互。

换句话说,听起来您需要std::remove_if std::find_if作为其谓词的一部分。

下面的方法使用lambda函数,因此需要C ++ 11。您可以通过InteractionChecker::operator()中的简单for循环来避免这种情况。

typedef std::vector<myObj*> ObjectVector;
struct InteractionChecker
{
  InteractionChecker(const ObjectVector& objects) : m_objects(objects) {}

  // Return 'true' iff checked_obj interacts with any object in m_objects.
  bool operator()(const myObj* checked_obj)
  {
    // Need to think about whether checked_obj interacts with itself
    return m_objects.end() != std::find_if(m_objects.begin(), m_objects.end(),
                                           [checked_obj](myObj* other)
                                           { 
                                               return checked_obj->interactsWith(other); 
                                           });
  } 

private:
  const ObjectVector& m_objects;
};

ObjectVector objects;
// Populate objects vector.

InteractionChecker checker(objects);
std::remove_if(objects.begin(), objects.end(), checker);

答案 2 :(得分:2)

因此,我不相信您当前的代码适用于所有情况。因此,我认为需要更大的解决方案。

例如,假设您的向量中有3个对象,并且所有3个对象都相互作用。您的代码将获取前两个,然后删除它们。当我们到达第三个时,没有任何东西可以与它进行交互,所以它将保留。 但那是不正确的行为

我写了一些代码如下:

  • 我们选择向量,并保持startend位置。这些迭代器代表了有效的剩余区域。

  • 我们向量中的三个区域:

    • [vec.begin(), start)之间的元素是我们知道将保留的对象,并且已经将它们与向量中的所有内容进行了比较。所以我们不再需要触摸它们了。
    • [start, end)之间的元素是我们仍然需要比较的东西,以查看它们是否有任何交互。
    • [end, vec.end())之间的元素是我们知道与其他事物交互的元素。但是,可能仍有更多元素与我们尚未找到的元素相互作用。
  • 我们的结束条件是start == end。这是有效的,因为在这一点上,没有更多的元素可以比较交互。

代码遵循:

#include <algorithm>
#include <iostream>
#include <vector>

struct myObj {
    myObj(int val): val(val) {}

    bool interactsWith(struct myObj *other) {
        return this->val == other->val;
    }

    const int val;
};

int main() {
    std::vector<myObj *> vec;
    vec.push_back(new myObj(3));
    vec.push_back(new myObj(2));
    vec.push_back(new myObj(1));
    vec.push_back(new myObj(2));

    // Print the vector
    for (auto x : vec)
        std::cout << x->val << " ";
    std::cout << "\n";

    auto start = vec.begin();
    auto end = vec.end();

    while (start != end) {
        // Test if start interacts with anything in the vector
        std::vector<myObj *>::iterator match;
        for (match = start + 1; match != vec.end(); ++match)
            if ((*start)->interactsWith(*match))
                break;

        // We did not find a match
        if (match == vec.end()) {
            start++;
            continue;
        }

        // If the match isn't already in the removal area, move it there.
        if (match < end)
            std::iter_swap(match, --end);

        // The start is always before the removal area, so move it there.
        std::iter_swap(start, --end);
    }

    // Print the vector
    for (auto x : vec)
        std::cout << x->val << " ";
    std::cout << "\n";

    // Delete the memory backing these elements
    // We should probably just be using std::vector<std::unique_ptr>>...
    std::for_each(end, vec.end(), [](myObj * & obj) {
        delete obj;
        obj = NULL;
        });

    // Remove the elements from the vector
    vec.erase(end, vec.end());

    // Print the vector
    for (auto x : vec)
        std::cout << x->val << " ";
    std::cout << "\n";
}

这将输出:

[4:04pm][wlynch@apple /tmp] ./foo
3 2 1 2   // The original vector
3 1 2 2   // The elements that intersect have been moved to the back of the vector
3 1       // We've erased those elements from the vector.

并注意:

这最终与NicholasM's answer非常相似,只有少数优化不是他的主张。

  • 一旦我们发现元素与任何东西都不相交,我们就可以忽略它。
  • 当我们找到交叉匹配时,我们可以将两个元素移动到列表的末尾。

允许这两种优化,因为intersectsWith()是关联的。