如何为地图创建自己的比较器?


83
typedef map<string, string> myMap;

在中插入新对时myMap,它将使用该键string通过自己的字符串比较器进行比较。是否可以覆盖该比较器?例如,我想通过键string的长度而不是字母来比较键。还是有其他方法可以对地图进行排序?

Answers:


140

std::map最多包含四个模板类型参数,第三个是比较器。例如:

struct cmpByStringLength {
    bool operator()(const std::string& a, const std::string& b) const {
        return a.length() < b.length();
    }
};

// ...
std::map<std::string, std::string, cmpByStringLength> myMap;

或者,您也可以将比较器传递给maps的构造方法

但是请注意,按长度比较时,映射中每个长度只能有一个字符串作为键。


4
请注意,如果我们要包含重复的密钥,则可以使用多图
Xitrum 2011年

@GeorgFritzsche您是否有机会提供将比较器传递给构造函数的示例?
bpeikes

1
@bpeikes:看起来没什么不同:std::map<std::string, std::string> myMap(cmpByStringLength());
Georg Fritzsche 2014年

我在使用std :: map <int,int>时遇到问题,有些问题的顺序增加,而另一些问题的顺序减小。我不想使用std :: map <int,int,std :: greater>和std :: map <int,int,std :: less>因为那样我就不能使用以不同顺序排序的地图作为单个函数的参数,除非我将所有内容都做成模板。我发现我必须执行以下操作:typedef std :: map <int,int,(bool)*(int,int)> mymap; 然后,我能够传递函数。我尝试了以下操作,但不起作用:typedef std :: map <int,int> mymap; mymap map1(std :: less); mymap map2(std :: greater);
bpeikes

2
@GeorgFritzsche:将比较器传递给构造函数无效,因为构造函数参数必须是比较器类型cmpByStringLength的实例,而不是的实例std::less<std::string>。对于可以在构造函数中设置任何比较器的通用映射,您需要类似std::map<std::string, std::string, std::function<bool(const std::string &, const std::string &)>> myMap(cmpByStringLength);
Chris Dodd

19

C ++ 11开始,您还可以使用lambda表达式来代替定义比较器struct:

auto comp = [](const string& a, const string& b) { return a.length() < b.length(); };
map<string, string, decltype(comp)> my_map(comp);

my_map["1"]      = "a";
my_map["three"]  = "b";
my_map["two"]    = "c";
my_map["fouuur"] = "d";

for(auto const &kv : my_map)
    cout << kv.first << endl;

输出:

1
2
3个
fouuur

我想重复一下Georg的答案的最后注解:按长度比较时,地图中每个长度只能有一个字符串作为键。

Ideone上的代码


12

是的,第三个模板参数onmap指定比较器,它是一个二进制谓词。例:

struct ByLength : public std::binary_function<string, string, bool>
{
    bool operator()(const string& lhs, const string& rhs) const
    {
        return lhs.length() < rhs.length();
    }
};

int main()
{
    typedef map<string, string, ByLength> lenmap;
    lenmap mymap;

    mymap["one"] = "one";
    mymap["a"] = "a";
    mymap["fewbahr"] = "foobar";

    for( lenmap::const_iterator it = mymap.begin(), end = mymap.end(); it != end; ++it )
        cout << it->first << "\n";
}

11
为什么派生自std::binary_function?需要吗?
Devolus '16

12
std::binary_function已在c ++ 17中删除,因此此答案可能使用更新。
丹·奥尔森

1

将比较函数的指针类型指定为映射的第3种类型,并提供指向映射构造函数的函数指针:
map<keyType, valueType, typeOfPointerToFunction> mapName(pointerToComparisonFunction);

请看下面的示例,该示例提供与的比较功能map,并以vector迭代器作为键和int值。

#include "headers.h"

bool int_vector_iter_comp(const vector<int>::iterator iter1, const vector<int>::iterator iter2) {
    return *iter1 < *iter2;
}

int main() {
    // Without providing custom comparison function
    map<vector<int>::iterator, int> default_comparison;

    // Providing custom comparison function
    // Basic version
    map<vector<int>::iterator, int,
        bool (*)(const vector<int>::iterator iter1, const vector<int>::iterator iter2)>
        basic(int_vector_iter_comp);

    // use decltype
    map<vector<int>::iterator, int, decltype(int_vector_iter_comp)*> with_decltype(&int_vector_iter_comp);

    // Use type alias or using
    typedef bool my_predicate(const vector<int>::iterator iter1, const vector<int>::iterator iter2);
    map<vector<int>::iterator, int, my_predicate*> with_typedef(&int_vector_iter_comp);

    using my_predicate_pointer_type = bool (*)(const vector<int>::iterator iter1, const vector<int>::iterator iter2);
    map<vector<int>::iterator, int, my_predicate_pointer_type> with_using(&int_vector_iter_comp);


    // Testing 
    vector<int> v = {1, 2, 3};

    default_comparison.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    default_comparison.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    default_comparison.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    default_comparison.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << default_comparison.size() << endl;
    for (auto& p : default_comparison) {
        cout << *(p.first) << ": " << p.second << endl;
    }

    basic.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    basic.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    basic.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    basic.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << basic.size() << endl;
    for (auto& p : basic) {
        cout << *(p.first) << ": " << p.second << endl;
    }

    with_decltype.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    with_decltype.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    with_decltype.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    with_decltype.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << with_decltype.size() << endl;
    for (auto& p : with_decltype) {
        cout << *(p.first) << ": " << p.second << endl;
    }

    with_typedef.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    with_typedef.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    with_typedef.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    with_typedef.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << with_typedef.size() << endl;
    for (auto& p : with_typedef) {
        cout << *(p.first) << ": " << p.second << endl;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.