Làm cách nào để tạo bộ so sánh của riêng tôi cho một bản đồ?


83
typedef map<string, string> myMap;

Khi chèn một cặp mới vào myMap, nó sẽ sử dụng khóa stringđể so sánh bằng bộ so sánh chuỗi của chính nó. Có thể ghi đè bộ so sánh đó không? Ví dụ, tôi muốn so sánh khóa stringtheo độ dài của nó, không phải theo bảng chữ cái. Hoặc có cách nào khác để sắp xếp bản đồ?

Câu trả lời:


140

std::mapcó tối đa bốn đối số kiểu mẫu, đối số thứ ba là đối số so sánh. Ví dụ:

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;

Ngoài ra, bạn cũng có thể chuyển một bộ so sánh cho hàm tạo mapcủa s .

Tuy nhiên, lưu ý rằng khi so sánh theo độ dài, bạn chỉ có thể có một chuỗi của mỗi độ dài trong bản đồ làm khóa.


4
lưu ý rằng chúng tôi có thể sử dụng Multimap nếu chúng ta muốn chứa trùng lặp chính
Xitrum

@GeorgFritzsche có bất kỳ cơ hội nào bạn cung cấp ví dụ về việc chuyển comparitor sang constructor không?
bpeikes

1
@bpeikes: Trông không quá khác biệt:std::map<std::string, std::string> myMap(cmpByStringLength());
Georg Fritzsche.

Tôi đã gặp sự cố với std :: map <int, int>, một số có thứ tự tăng và những thứ khác theo thứ tự giảm. Tôi không muốn sử dụng std :: map <int, int, std :: great> và std :: map <int, int, std :: less> vì sau đó tôi không thể sử dụng các bản đồ được sắp xếp theo các thứ tự khác nhau dưới dạng các tham số cho một hàm duy nhất trừ khi tôi tạo mọi thứ thành mẫu. Tôi thấy rằng tôi phải làm như sau: typedef std :: map <int, int, (bool) * (int, int)> mymap; Sau đó, tôi đã có thể vượt qua các chức năng. Tôi đã thử cách sau, nhưng nó không hoạt động: typedef std :: map <int, int> mymap; mymap map1 (std :: less); mymap map2 (std :: lớn hơn);
bpeikes

2
@GeorgFritzsche: Điều đó sẽ không hoạt động khi truyền bộ so sánh cho hàm tạo, vì đối số của hàm tạo phải là một thể hiện của kiểu so sánh và cmpByStringLengthkhông phải là một thể hiện của std::less<std::string>. Đối với một bản đồ chung có thể có bất kỳ bộ so sánh trong các nhà xây dựng, bạn cần một cái gì đó giống nhưstd::map<std::string, std::string, std::function<bool(const std::string &, const std::string &)>> myMap(cmpByStringLength);
Chris Dodd

19

Kể từ C ++ 11 , bạn cũng có thể sử dụng biểu thức lambda thay vì xác định cấu trúc so sánh:

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;

Đầu ra:

1
hai
ba
fouuur

Tôi muốn nhắc lại lưu ý cuối cùng trong câu trả lời của Georg: Khi so sánh theo độ dài, bạn chỉ có thể có một chuỗi của mỗi độ dài trong bản đồ làm khóa.

Mã trên Ideone


12

Có, tham số mẫu thứ 3 trên mapchỉ định bộ so sánh, là một vị từ nhị phân. Thí dụ:

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
Tại sao bắt nguồn từ std::binary_function? Nó có cần thiết không?
Devolus

12
std::binary_functionbị xóa trong c ++ 17 nên câu trả lời này có thể sử dụng cập nhật.
Dan Olson

1

Chỉ định loại con trỏ cho hàm so sánh của bạn làm loại thứ 3 trong bản đồ và cung cấp con trỏ hàm cho hàm tạo bản đồ:
map<keyType, valueType, typeOfPointerToFunction> mapName(pointerToComparisonFunction);

Hãy xem ví dụ dưới đây để cung cấp một hàm so sánh với a map, với vectortrình vòng lặp là khóa và intlà giá trị.

#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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.