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

std::set_intersection on two completely different containers

本文关键字:两个 interaction set std      更新时间:2023-10-16

我有一个简单的要求,需要从另一个向量中的字符串主列表中查找一个向量的字符串出现情况。一开始我很容易做到:

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列表中的每个字符串都与一个标识符相关联。我希望我可以使用std::set_intersection,这样我就可以使用target_list中相交的元素作为索引来获取它们的标识符。实际上,我想我应该把master_list改成一张地图,就像这样:

map<string, SomeCustomId> master_list;

并且能够做一些类似的事情:

auto I_want_this_id = master_list[target_list[0]);    

但现在,即使我编写了自己的比较函数,我也不确定是否可以使用set_interaction来比较两个完全不同的容器(custom_list,一个向量和master_list,一个映射(。类似于:

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

这不太奏效(我遇到了各种编译器错误(,而且凭直觉,我也觉得有些不对劲

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

std::set_intersection期望比较器在lhs < rhs的情况下返回true,而不是在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;
    }
};

演示。

编辑:更新了演示代码,包括所有必要的标题。(缺少<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.Lange,那就容易多了。简单地说:

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

不需要自定义谓词,而且可读性很强。演示。

算法实际上并不关心容器。他们关心迭代器,只要这两种容器类型都满足算法的迭代器要求,并且元素类型与比较器匹配,兼容性就不会成为问题。

所以,从根本上说,你所做的一切都是可以的

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