从std :: vector

时间:2019-02-11 14:01:52

标签: c++ stdvector

我有以下模式:

  1. 我有一个std::vector,其中包含指向对象的原始指针(我知道原始指针是“邪恶的”,但这是需要维护的旧版软件)。
  2. 现在对于向量中的每个元素,我需要进行一次测试,如果测试是肯定的,请使用指针执行某些操作,将其删除,然后将其从向量中删除:

伪代码:

for each pointer in vector
{
  if (SomeTest(pointer))
  {
     DoSomething(pointer)
     delete pointer
     remove pointer from vector
  }
}

我无法为此提供一些清晰的代码。

This link提供了不同的方法,但对我来说,它们都多少有些麻烦。

我现在正在使用的令人讨厌的解决方案:

for(auto &  p : v)
{
   if (SomeTest(p))
   {
       DoSomething(p);
       delete p;
       p = nullptr;
   }
}

v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());

7 个答案:

答案 0 :(得分:28)

答案通常是:了解您的<algorithm>(这对我自己有很好的提醒作用);)

std::partition是您要查找的内容:std::partition(begin, end, p)“移动” 不需要的范围[[beginend)的元素满足范围结尾处的谓词p;然后可以将它们当作批处理。

auto const to_be_removed = std::partition(begin(v), end(v), [](auto p){ /* predicate */ });
std::for_each(to_be_removed, end(v), [](auto p) {
    /* crunch */
    delete p;
});
v.erase(to_be_removed, end(v));

完整程序

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

int main()
{
    std::vector v = { new int{0}, new int{1}, new int{2} };

    // let's delete all even values
    auto const to_be_removed = std::partition(begin(v), end(v), [](auto p){ return *p % 2 != 0; });
    std::for_each(to_be_removed, end(v), [](auto p) {
        std::cout << "Deleting value " << *p << "...\n";
        delete p;
    });
    v.erase(to_be_removed, end(v));
}

Live demo

走得更远

此实现有两个主要缺点:向量的顺序不稳定(1),可以将其分解为可重用函数(2)。

  • (1)由std::stable_partition解决。
  • (2)并不难:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
{
    auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
    std::for_each(to_be_removed, end, [d](auto p) { d(p) ; delete p; });
    return to_be_removed;
}

template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
{
    using std::begin, std::end;
    return c.erase(delete_if(begin(c), end(c), p, d), end(c));
}

用法

delete_if(v, SomeTest, DoSomething);

Live demo

答案 1 :(得分:13)

您可以使用std::remove_if,我不确定您链接的文章为何在删除指针之前使用std::remove_if ,因为那样行不通。您必须在删除之前删除指针

std::vector<int*> v;

v.erase(std::remove_if(std::begin(v), std::end(v), [](int* p){

    // do your test and do not remove on failure
    if(!SomeTest(p))
        return false; // keep this one

    DoSomething(p);

    // Now we remove but be sure to delete here, before the
    // element is moved (and therefore invalidated)

    delete p;

    return true; // signal for removal

}), std::end(v));

注意:为什么这样做很安全。

删除指针不会修改指针本身,而是指向的对象。这意味着该方法不会修改任何元素。

C++17 28.6.8 5的标准保证谓词对于每个元素仅被调用一次。

答案 2 :(得分:6)

从链接的文章开始,最简单的解决方案是采用erase_if函数

template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
{
    c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
}

然后用

调用
erase_if(v, [](T *pointer)
         {
           if (SomeTest(pointer))
           {
              DoSomething(pointer);
              delete pointer;
              return true; //remove pointer from vector
           }
           return false;
         });

如果要将SomeTest / DoSomething部分与delete部分分开,显然可以将谓词一分为二:

template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
{
    auto e = std::remove_if(std::begin(c), std::end(c),
        [&p](Container::value_type *pointer)
        {
          if (p(pointer)) {
            delete pointer;
            return true;
          }
          return false;
        });
    c.erase(e, std::end(c));
}

由于您没有说过为什么,您不喜欢自己链接的erase_if,所以我无法猜测这是否有相同的问题。

答案 3 :(得分:2)

使用以下方法,首先分割要删除的元素,然后删除,然后调整向量。

auto badIt = std::stable_partition(std::beging(v), std::end(v), SomeTestInverse);
std::for_each(badIt, std::end(v), [](auto e){ DoSomething(e); delete e;});
v.erase(badIt,std::end(v));

提供的谓词必须为true才能使元素保持正常工作,因为未通过谓词的元素处于最后一个范围。

答案 4 :(得分:0)

假设您有一个int指针向量。这是我的解决方案:

vector<int*> vpi;

for (vector<int*>::iterator it = vpi.begin(); it != vpi.end(); )
{
    if (SomeTest(*it))
    {
        DoSomething(*it)
        int* old = *it;
        it = vpi.erase(it);
        delete old;
    } else
    {
       it++;
    }
}

答案 5 :(得分:0)

我会使用的一些方法:

for (auto i = vector.begin(); i != vector.end(); ++i) {
  if (SomeTest(*i)) {
    DoSomething(*i);
    delete *i;
    *i = nullptr;
  }
}
vector.erase(std::remove(vector.begin(), vector.end(), nullptr), vector.end());

答案 6 :(得分:0)

保持简单。亚尼没有理由在可能的情况下解决这个问题的更为通用和复杂的版本(提示:您不需要),或者寻找晦涩的STL方法(嗯,除非您希望 )。

import pandas as pd

df['Date'] = '1/1/2020'
df['Date'] = pd.to_datetime(df['Date'])              #returns 2020-01-01
df['NewDate'] = df.Date - pd.DateOffset(months=3)    #returns 2019-10-01 <---- answer

# For fun, change it to FY Quarter '2019Q4'
df['NewDate'] = df['NewDate'].dt.year.astype(str) + 'Q' + df['NewDate'].dt.quarter.astype(str)

# Convert '2019Q4' back to 2019-10-01
df['NewDate'] = pd.to_datetime(df.NewDate)