【问题标题】:How to remove an element from std::vector if the element id matches a search parameter如果元素 id 与搜索参数匹配,如何从 std::vector 中删除元素
【发布时间】:2015-10-13 16:53:23
【问题描述】:

我正在尝试编写一个算法,如果项目 ID 与参数匹配,它将搜索并从项目向量中删除项目。请参阅下面的示例代码:

struct item{
    item(int newID){id = newID;}
    bool operator==(const item& other){return id = other.id;}
    int id
};

std::vector<std::unique_ptr<item>> vec;

vec.push_back(std::unique_ptr<item>(new item(10));
vec.push_back(std::unique_ptr<item>(new item(15));
vec.push_back(std::unique_ptr<item>(new item(20));

所以,使用上面的代码,我希望能够搜索存储值 15 的项目,并将其从向量中删除,将其删除。

我该怎么做呢?

诚然,我可能还需要复习唯一指针的使用,所以如果我的语法不正确,请随时纠正我。

我尝试过的一些解决方案如下:

void remove_item(int id){
    vec.erase(
               std::remove_if(
                               vec.begin(),
                               vec.end(),
                               [](const item& e){
                                     return id==e.id;
                               }),
               vec.end()
              );

上面的代码产生一个错误,指出变量 id 不是 lambda 表达式的捕获列表的一部分。

其次,我试过了:

void remove_item(item e){
    auto iter = std::find(vec.begin(), vec.end(), e);
    vec.erase(iter);
}

本例中的上述代码在 == 运算符成员函数中产生类型不匹配错误。

【问题讨论】:

  • 到目前为止你有没有尝试过?有大量关于此的信息
  • 是的,我尝试过使用 remove_if lambda 表达式,这会导致编译器抱怨搜索参数不是捕获列表的一部分。我还尝试使用带有谓词函数的 std find 表达式,以及项目类上的 == 运算符。这些都不起作用
  • 您应该展示这一点,以便我们为您提供帮助。仅仅问你怎么做就太宽泛了,因为有很多方法可以做到这一点。
  • 向我们展示你的 lambda!请记住,removeremove_if 实际上并没有删除任何内容。见en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
  • 我将更新问题以显示我尝试的一些解决方案

标签: c++ vector smart-pointers


【解决方案1】:

您需要将id 添加到 lambda 的捕获列表中,以便它可以访问它。然后您需要将传递给 lambda 的类型设为 *vec.begin() 的类型,即 std::unique_ptr&lt;item&gt; 而不是 item

void remove_item(int id){
    vec.erase(
               std::remove_if(
                               vec.begin(),
                               vec.end(),
                               [id](const std::unique_ptr<item>& e){
                                     return id==e->id;
                               }),
               vec.end()
              );
}

从你的代码中删除所有其他无关的错误,你会得到类似的东西:

struct item {
    item(int newID) { id = newID; }
    bool operator==(const item& other) { return id == other.id; } // == here not =                                          
    int id;
};

std::vector<std::unique_ptr<item>> vec;

void remove_item(int id) {
    vec.erase(std::remove_if(
        vec.begin(), vec.end(), [id](const std::unique_ptr<item>& e) 
                                    {   return id == e->id; })
        ,vec.end());
}

int main()
{
    vec.push_back(std::unique_ptr<item>(new item(10))); // was missing a close paren
    vec.push_back(std::unique_ptr<item>(new item(15))); // was missing a close paren
    vec.push_back(std::unique_ptr<item>(new item(20))); // was missing a close paren
    remove_item(15);
    for (const auto & e : vec)
        std::cout << e->id << " ";
}

Live Example

【讨论】:

  • ...即使那样代码也不起作用,因为你有 id==e.id; 而不是 id==e-&gt;id;
  • 我猜这里不需要“==”的重载
【解决方案2】:

使用remove-erase idiom:

// search for this value
const int val = 15;

// use lambda capture to get "val" into lambda
auto lambda = [val] (const std::unique_ptr<item> & a) { return a->id == val;};

// use remove-erase idiom
auto rem = std::remove_if(vec.begin(), vec.end(), lambda);
vec.erase(rem, vec.end());

【讨论】:

  • 此代码从向量中删除指针。但是它指向的内存也会被释放吗?
【解决方案3】:

正如 Fred Larson 所说,您应该使用“erase remove_if”习语。 例如:

std::vector<std::unique_ptr<item>> vec;
vec.push_back(std::make_unique<item>(10));
vec.push_back(std::make_unique<item>(15));
vec.push_back(std::make_unique<item>(20));
vec.erase(std::remove_if(vec.begin(), vec.end(),
  [](std::unique_ptr<item> const& item) -> bool { return item->id > 10; }), vec.end());
std::cout << vec.size(); // Output will be 1

【讨论】:

  • 这个问题是我需要能够搜索和删除不基于硬编码值的项目(在您的示例中为 10)
  • 您可以使用 lambda 捕获来执行此操作。 ;)
【解决方案4】:

首先,我想声明,对于C++ containers,我更喜欢emplace_backemplace,而不是push_backinsert。我这么说是因为不是这样写:-

vector<unique_ptr<item>> v;
v.push_back(unique_ptr<item>(new item(5));

你可以简单地这样做:-

vector<unique_ptr<item>> v;
v.emplace_back(new item(5));

其次,您需要知道是要删除值为 15 的“一个项目”还是“所有项目”。这是因为对于前者,您需要使用 find_if,而对于第二种情况,您应该使用 @ 987654329@。您也可以在第一种情况下使用remove_if(但在第二种情况下不能使用find_if),但不建议这样做,因为remove_if 会移动第一种情况下不需要的元素。加上find_if 只是将迭代器返回到条件第一次匹配的项目。因此,选择权在您手中,请谨慎使用。

代码如下:-

// find_if
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
using namespace std;

struct item
{
    int x;
    item(){}
    item(int x)
    {
        this->x=x;
    }
};

int main()
{
    vector<unique_ptr<item>> v;
    v.emplace_back(new item(15));
    v.emplace_back(new item(5));
    v.emplace_back(new item(7));
    v.emplace_back(new item(15));
    v.emplace_back(new item(27));
    v.emplace_back(new item(15));
    v.emplace_back(new item(80));

    cout<<"deleting the item storing value 15...\n";
    auto itr = find_if(v.begin(), v.end(), [](unique_ptr<item> &u)     // you cant copy two `unique_ptr` as they can always be `moved & not copied`, hence we are `passing them by reference`
    {
        return (u->x==15);
    });
    v.erase(itr);    // as you need to remove only one item so specify the `iterator` pointing to the required item in `erase`

    for (auto &u:v)  see the use of `reference` again as without it the compiler throws error messages
    {
        cout<<u->x<<" ";
    }
    return 0;
}

输出将是:-

deleting the item storing value 15...
5 7 15 27 15 80

对于remove_if,您只需要进行以下修改:-

auto itr = remove_if(v.begin(), v.end(), [](unique_ptr<item> &u)
    {
        return (u->x==15);
    });
    v.erase(itr,v.end());    // as you need to delete a range of items so you need to specify a range (here `itr` to `v.end()`) in `erase`

这里的输出将是:-

deleting the items storing value 15...
5 7 27 80

现在我想你的疑虑会得到很好的解决!!!

【讨论】: