【发布时间】:2011-08-09 15:33:23
【问题描述】:
typedef map<string, string> myMap;
当向myMap 插入一个新的pair 时,它将使用键string 来通过它自己的字符串比较器进行比较。是否可以覆盖该比较器?例如,我想通过长度而不是字母来比较键 string。或者还有其他方法可以对地图进行排序吗?
【问题讨论】:
typedef map<string, string> myMap;
当向myMap 插入一个新的pair 时,它将使用键string 来通过它自己的字符串比较器进行比较。是否可以覆盖该比较器?例如,我想通过长度而不是字母来比较键 string。或者还有其他方法可以对地图进行排序吗?
【问题讨论】:
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 constructor。
但是请注意,当按长度进行比较时,地图中每个长度只能有一个字符串作为键。
【讨论】:
std::map<std::string, std::string> myMap(cmpByStringLength());
cmpByStringLength 不是std::less<std::string> 的实例。对于可以在构造函数中设置任何比较器的通用地图,您需要类似 std::map<std::string, std::string, std::function<bool(const std::string &, const std::string &)>> myMap(cmpByStringLength);
由于C++11,您也可以使用lambda expression 而不是定义比较器结构:
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
两个
三
呵呵
我想重复一下 Georg 回答的最后一点:按长度比较时,地图中每个长度只能有一个字符串作为键。
【讨论】:
是的,map 上的第三个模板参数指定了比较器,它是一个二元谓词。示例:
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";
}
【讨论】:
std::binary_function?需要吗?
std::binary_function 在 c++17 中被删除,所以这个答案可能会使用更新。
将指向您的比较函数的指针的类型指定为映射中的第 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;
}
}
【讨论】:
decltype(int_vector_iter_comp)* 太棒了!谢谢!!