C++ 如何返回包含不在集合中的元素的向量副本?

C++ 如何返回包含不在集合中的元素的向量副本?,c++,stl,c++11,C++,Stl,C++11,假设我有以下两种数据结构: std::vector<int> all_items; std::set<int> bad_items; 假设所有项目,坏项目,是坏项目和项目()都是某些包含类的一部分。有没有一种方法可以编写它们items()getter,以便: 方法中不需要临时变量 它不需要自定义functor,struct is\u item\u bad 我本来希望在std::set上使用count方法作为一个函子,但我还没有找到正确的方法来表达remove\u c

假设我有以下两种数据结构:

std::vector<int> all_items;
std::set<int> bad_items;
假设
所有项目
坏项目
是坏项目
项目()
都是某些包含类的一部分。有没有一种方法可以编写它们
items()
getter,以便:

  • 方法中不需要临时变量
  • 它不需要自定义functor,
    struct is\u item\u bad
我本来希望在
std::set
上使用
count
方法作为一个函子,但我还没有找到正确的方法来表达
remove\u copy\u if
算法

编辑:修复了
items()
中的逻辑错误。实际代码没有问题,这是一个转录错误

EDIT:我接受了一个不使用
std::set_difference
的解决方案,因为它更通用,即使
std::vector
没有排序,也能工作。我选择在代码中使用C++0x lambda表达式语法。我的最后一个
items()
方法如下所示:

std::vector<int> items() const {
    std::vector<int> good_items;
    good_items.reserve(all_items.size());
    std::remove_copy_if(all_items.begin(), all_items.end(),
                        std::back_inserter(good_items),
                        [&bad_items] (int const i) {
                            return bad_items.count(i) == 1;
                        });
}
std::vector items()常量{
标准::矢量良好_项;
好的物品。保留(所有物品。大小());
std::remove_copy_if(所有项目.begin(),所有项目.end(),
标准:背部插入器(良好项目),
[&坏项](int const i){
返回坏项。计数(i)=1;
});
}

在大约800万项的向量上,上述方法以3.1秒运行。我在测试台上标记了
std::set_difference
方法,并在大约2.1s内运行。感谢所有提供了精彩答案的人。

std::remove\u copy\u if
向目标集合返回迭代器。在这种情况下,它将返回
good\u items.end()
(或类似的内容)
good_items
在方法末尾超出范围,因此这将导致一些内存错误。您应该返回
好项目
或通过引用传入
新向量
,然后
清除
调整大小
,并填充它。这将消除临时变量


我相信您必须定义自定义函子,因为该方法依赖于对象
bad\u items
,如果没有它,您就无法指定它。

正如jeffamaphone建议的那样,如果您可以对任何输入向量进行排序,您可以使用
std::set\u difference
,这是一种高效且代码更少的方法:

#include <algorithm>
#include <set>
#include <vector>

std::vector<int> 
get_good_items( std::vector<int> const & all_items,
                std::set<int> const & bad_items )
{
    std::vector<int> good_items;

    // Assumes all_items is sorted.
    std::set_difference( all_items.begin(),
                         all_items.end(),
                         bad_items.begin(),
                         bad_items.end(),
                         std::back_inserter( good_items ) );

    return good_items;
}
#包括
#包括
#包括
向量
获取好的项目(标准::向量常量和所有项目,
std::设置常量和错误项)
{
标准::矢量良好_项;
//假设所有_项都已排序。
std::set_difference(所有_项.begin(),
所有_项。结束(),
错误的\u项。开始(),
错误的\u项。结束(),
标准:背部插入器(良好项目);
归还好的物品;
}

由于函数将返回一个向量,因此在任何情况下都必须创建一个新向量(即复制元素)。在这种情况下,
std::remove\u copy\u如果
没有问题,但您应该正确使用它:

#include <iostream>
#include <vector>
#include <set>
#include <iterator>
#include <algorithm>
#include <functional>
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
        std::vector<int> result;
        remove_copy_if(all.begin(), all.end(), back_inserter(result),
                  [&bad](int i){return bad.count(i)==1;});
        return result;
}

int main()
{
        std::vector<int> all_items = {4,5,2,3,4,8,7,56,4,2,2,2,3};
        std::set<int> bad_items = {2,8,4};
        std::vector<int> filtered_items = filter(all_items, bad_items);
        copy(filtered_items.begin(), filtered_items.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << std::endl;
}
在任何情况下,显式函数对象都更具可读性,我认为:

struct IsMemberOf {
        const std::set<int>& bad;
        IsMemberOf(const std::set<int>& b) : bad(b) {}
        bool operator()(int i) const { return bad.count(i)==1;}
};
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
        std::vector<int> result;
        remove_copy_if(all.begin(), all.end(), back_inserter(result), IsMemberOf(bad));
        return result;
}
struct IsMemberOf{
const std::set&bad;
IsMemberOf(const std::set&b):坏(b){}
bool运算符()(int i)const{return bad.count(i)==1;}
};
标准::向量过滤器(常数标准::向量和全部,常数标准::集和坏)
{
std::向量结果;
如果(all.begin()、all.end()、back_inserter(result)、IsMemberOf(bad)),则删除_copy_;
返回结果;
}

冒着显得过时的风险:


std::set<int> badItems;
std::vector<int> items;
std::vector<int> goodItems;
for ( std::vector<int>::iterator iter = items.begin();
      iter != items.end();
      ++iter)
{
   int& item = *iter;
   if ( badItems.find(item) == badItems.end() )
   {
      goodItems.push_back(item);
   }
}

std::设置坏项;
向量项;
std::向量项;
for(std::vector::iterator iter=items.begin();
iter!=items.end();
++国际热核聚变实验堆(iter)
{
int&item=*iter;
if(badItems.find(item)=badItems.end())
{
好的物品。推回(物品);
}
}

也许您可以使用std::set\u difference?如果没有重复对象,则可以为此使用两个集合和集合操作。但我想你需要一个向量。@dribeas:我很确定他说:“……如果你能对任何输入向量进行排序……”的意思是,向量是有序的。数据以一种有序的方式到达我这里,我将其插入到向量中。我没有为此使用集合,因为我不想为已经自然排序的数据添加额外的集合比较器开销。这是个错误吗?不,Lrm,那很好,只要向量中的顺序与使用集合而不是向量时的顺序相同。+1。。。显然,因为这与我自己写的差不多,所以更完整!库比,谢谢你的详细回答。我已经修复了逻辑错误——这是一个转录错误,在原始代码中不存在。我认为你的可读性是对的。你能再解释一下你的第一个解决方案吗?我不熟悉函子[&bad](inti)的语法。。。这是在创建某种lambda表达式吗?@lrm:是的,第一个示例使用C++0x lambda表达式。它构建一个函数对象,通过引用捕获“bad”,并公开
bool operator()(int i)const
——与您最初编写的一样,只是使用引用而不是指针。它使用GCC4.5+(我想还有MSVC10)编译。第二位使用C++TR1通用绑定(可在C++0x模式下的gcc 4.4+的std命名空间中使用,也可作为boost.bind用于所有C++98编译器),第三位使用基本C++98.0。这是我的输入错误。实际代码确实返回
好的\u项目
。我已经在问题中解决了。谢谢你的回答,克雷格。我正在尝试使用适当的STL算法,因为它是Meyer有效STL的第43项。@lrm:“适当的STL算法”。。。STL算法/等太多,普通人无法记忆
struct IsMemberOf {
        const std::set<int>& bad;
        IsMemberOf(const std::set<int>& b) : bad(b) {}
        bool operator()(int i) const { return bad.count(i)==1;}
};
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
        std::vector<int> result;
        remove_copy_if(all.begin(), all.end(), back_inserter(result), IsMemberOf(bad));
        return result;
}

std::set<int> badItems;
std::vector<int> items;
std::vector<int> goodItems;
for ( std::vector<int>::iterator iter = items.begin();
      iter != items.end();
      ++iter)
{
   int& item = *iter;
   if ( badItems.find(item) == badItems.end() )
   {
      goodItems.push_back(item);
   }
}