两个完全不同的容器上的std :: set_intersection

时间:2014-05-24 15:32:24

标签: c++ stl set stl-algorithm set-intersection

我有一个简单的要求,我需要从另一个向量中的字符串主列表中找到一个向量中字符串的出现。我最初能够轻松地完成这项工作:

vector<string> custom_list;
set<string> master_list;
vector<string> target_list;

std::sort(custom_list.begin(), custom_list.end());
std::set_intersection(custom_list.begin(), custom_list.end(), master_list.begin(),
                      master_list.end(), back_inserter(target_list));

这很好用。但后来发现master_list中的每个字符串都与一个标识符相关联。我希望我能以这样的方式使用std :: set_intersection,我可以使用target_list中的相交元素作为索引来获取它们的标识符。实际上我以为我会将master_list更改为地图,如下所示:

map<string, SomeCustomId> master_list;

并且可以执行以下操作:

auto I_want_this_id = master_list[target_list[0]);    

但是现在我不确定我是否可以使用set_intersection来比较两个完全不同的容器(custom_list,vector和master_list,map),即使我自己编写比较函数也是如此。类似的东西:

struct mycomparer {
    bool operator()(string const& lhs, pair<string, SomeCustomId> const& rhs) {
        return lhs == rhs.first;
    }
};

这不是很有效(我有各种各样的编译器错误)而且直觉上也是如此,对我来说感觉不对。

有没有更好的方法来完成我想要做的事情?

2 个答案:

答案 0 :(得分:4)

std::set_intersection期望比较器在true时返回lhs < rhs,而不是lhs == rhs。它也必须能够比较它的两个参数而不管顺序(毕竟,确定参数是否相等是由(!comp(a, b) && !comp(b, a))完成的)。

因此,您需要类似

的内容
struct mycomparer {
    bool operator()(string const& lhs, pair<string const, SomeCustomId> const& rhs) {
        return lhs < rhs.first;
    }
    bool operator()(pair<string const, SomeCustomId> const& lhs, string const& rhs) {
        return lhs.first < rhs;
    }
};

Demo

编辑:更新了演示代码以包含所有必需的标题。 (<iterator><string>丢失了。它们可能包含在GCC中的其他标题中,但不包含在VC ++中。)

VC ++ 2012,在进行调试构建时,似乎对提供的谓词运行了一些额外的测试。这会导致编译失败,并出现error C2664: 'bool mycomparer::operator ()(const std::pair<_Ty1,_Ty2> &,const std::string &)' : cannot convert parameter 1 from 'std::basic_string<_Elem,_Traits,_Alloc>' to 'const std::pair<_Ty1,_Ty2> &'之类的错误。 (一旦标题被修复并且我切换到旧的初始化样式,它在发布版本上编译就好了。)

要解决此问题,请提供operator ()的过载,并采用所有四种可能的参数组合:

struct mycomparer {
    bool operator()(string const& lhs, pair<string const, SomeCustomId> const& rhs) {
        return lhs < rhs.first;
    }
    bool operator()(pair<string const, SomeCustomId> const& lhs, string const& rhs) {
        return lhs.first < rhs;
    }
    bool operator()(string const& lhs, string const& rhs) {
        return lhs < rhs;
    }
    bool operator()(pair<string const, SomeCustomId> const& lhs,
                    pair<string const, SomeCustomId> const& rhs) {
        return lhs.first < rhs.first;
    }
};

编辑2:如果你可以使用Boost.Range,那就容易多了。简单地:

boost::set_intersection(custom_list, 
                        master_list | boost::adaptors::map_keys,
                        back_inserter(target_list));

不需要自定义谓词,也非常易读。 Demo

答案 1 :(得分:1)

算法并不真正关心容器。他们关心迭代器,只要两个容器类型都满足算法的迭代器要求,并且元素类型与比较器匹配,兼容性不应成为问题。

所以,从根本上说,你正在做的事情还可以。

您需要更正比较器中的逻辑; operator()应该实现一个小于谓词。而且,正如T.C.指出,你需要明确地实现反向比较,因为元素类型不能隐式地相互转换。