我想用 C++ 编写一个通用排序器。我有一些这样的代码。
template<typename T>
class sorter {
public:
template<typename R>
bool RegisterDimValueFetcher(const std::string &name, const std::function<R(const T &)> &f)
{
if (!f) {
return false;
}
bool inserted = fetcher_map.insert(std::make_pair(name, f)).second;
if (inserted) {
priority_list.emplace_back(name);
}
return inserted;
}
bool AdjustPriority(const std::vector<std::string> &priorityList) {
priority_list.assign(priorityList.begin(), priorityList.end());
return priority_list == priorityList;
}
template<template<typename...> class C>
bool sort(C<T> &collection, bool asc = true, typename std::enable_if<is_container<C<T>>::value>::type* = nullptr) {
std::sort(collection.begin(), collection.end(), [&](const T &lRef, const T &rRef) {
for (auto &dim : priority_list) {
auto pair_iter = fetcher_map.find(dim);
if (pair_iter != fetcher_map.end()
&& pair_iter->second) {
auto l_val = pair_iter->second(lRef);
auto r_val = pair_iter->second(rRef);
if (l_val != r_val) {
return asc ? std::less<R>()(l_val, r_val) : std::less<R>()(r_val, l_val); // here using the template type R and it's obviously wrong.
}
}
}
return true;
});
return true;
}
private:
std::unordered_map<std::string, std::function<R(const T &)>> fetcher_map; // template type R is not declared, but if it declared, current class could not support different return type
std::vector<std::string> priority_list;
};
我希望这段代码能够满足我的期望,使用代码如下:
struct Student {
Student(const std::string &stuName, size_t stuAge)
: name(stuName), age(stuAge) { }
Student(const Student &ref)
: name(ref.name), age(ref.age) { }
std::string name;
size_t age;
};
std::ostream &operator << (std::ostream &os, Student &stu) {
os << "{name:" << stu.name << ", age:" << stu.age << "]";
return os;
}
int func() {
std::vector<Student> students;
students.emplace_back(std::string("n1"), 36u);
students.emplace_back(std::string("n2"), 36u);
students.emplace_back(std::string("n2"), 37u);
students.emplace_back(std::string("n3"), 2u);
sorter<std::string, Student> gsorter;
gsorter.RegisterDimValueFetcher("name", [](const Student &stu) {
return stu.name;
});
gsorter.RegisterDimValueFetcher("age", [](const Student &stu) {
return stu.age;
//return std::to_string(stu.age);
});
gsorter.sort(students, false);
for (auto &stu : students) {
std::cout << stu << std::endl;
}
std::cout << std::endl;
gsorter.AdjustPriority({"age", "name"});
gsorter.sort(students);
for (auto &stu : students) {
std::cout << stu << std::endl;
}
return 0;
}
并得到如下内容的结果:
{name:n3, age:2}
{name:n2, age:37}
{name:n2, age:36}
{name:n1, age:36}
{name:n3, age:2}
{name:n1, age:36}
{name:n2, age:36}
{name:n2, age:37}
我尝试将 R 作为在模板类型 T 之后声明的模板类型,这导致返回类型更改为指定类型,如第二个 lambda 中的注释行。
任何人都可以帮助我改进代码吗?我的期望是使类排序器成为 mysql 中关键字“ORDER BY”的实现。
如果您存储(三向)comparers而不是getters,您可能会摆脱模板返回类型:
template<typename T>
class sorter
{
public:
bool RegisterComparer(const std::string &name,
const std::function<int(const T&, const T&)> &f)
{
if (!f) {
return false;
}
bool inserted = comparer_map.insert(std::make_pair(name, f)).second;
if (inserted) {
priority_list.emplace_back(name);
}
return inserted;
}
template<template<typename...> class C,
typename std::enable_if<is_container<C<T>>::value, bool>::type = false >
bool sort(C<T> &collection, bool asc = true) const {
std::sort(collection.begin(), collection.end(), [&](const T &lRef, const T &rRef) {
for (auto &dim : priority_list) {
auto pair_iter = comparer_map.find(dim);
if (pair_iter != comparer_map.end() && pair_iter->second) {
const auto comp = (pair_iter->second)(lRef, rRef) * (asc ? 1 : -1);
if (comp == 0) continue;
return comp < 0;
}
}
return false;
});
return true;
}
private:
std::unordered_map<std::string, std::function<int(const T&, const T&)>> comparer_map;
std::vector<std::string> priority_list;
};
sorter<Student> gsorter;
gsorter.RegisterComparer("name", [](const Student &lhs, const Student &rhs) {
return lhs.name.compare(rhs.name);
});
gsorter.RegisterComparer("age", [](const Student &lhs, const Student &rhs) {
return (int) lhs.age - (int) rhs.age;
});