C++中存储不同返回类型std::function并实现类MySQL ORDER BY通用排序器
问题与解决方案
问题1:能否在C++中将多个返回类型未指定的std::function放入同一个容器?
可以实现,但不能直接存储不同返回类型的std::function(因为它们属于不同类型),需要通过类型擦除技术统一接口:
- 定义一个抽象基类,提供通用的调用/比较接口,屏蔽具体返回类型的差异;
- 用模板子类封装不同返回类型的
std::function,实现基类的通用接口; - 容器存储基类的智能指针(如
std::unique_ptr),从而实现不同类型函数对象的统一管理; - C++17及以上也可使用
std::any存储不同类型的std::function,但调用时需要显式恢复类型,灵活性不如基类继承的方式。
问题2:实现类似MySQL ORDER BY的通用排序器
原代码的核心问题是无法存储不同返回类型的取值函数,且排序时无法适配多种类型的比较逻辑。以下是改进后的完整实现:
辅助工具:容器判断Trait
#include <iostream> #include <vector> #include <unordered_map> #include <functional> #include <algorithm> #include <memory> #include <string> // 判断是否为容器的辅助Trait template<typename T> struct is_container { private: template<typename C> static char test(typename C::iterator*); template<typename C> static int test(...); public: static const bool value = sizeof(test<T>(nullptr)) == sizeof(char); };
改进后的通用排序器
template<typename T> class sorter { public: // 抽象基类:统一不同返回类型的取值器接口 class FetcherBase { public: virtual ~FetcherBase() = default; // 比较两个T对象的当前维度,返回l是否应排在r前面(升序规则) virtual bool compare(const T& l, const T& r, bool asc) const = 0; }; // 具体取值器模板:封装特定返回类型的取值与比较逻辑 template<typename R> class Fetcher : public FetcherBase { public: using FuncType = std::function<R(const T&)>; Fetcher(FuncType f) : func_(std::move(f)) {} bool compare(const T& l, const T& r, bool asc) const override { R l_val = func_(l); R r_val = func_(r); if (l_val != r_val) { return asc ? (l_val < r_val) : (r_val < l_val); } return false; // 维度值相等,继续比较下一个维度 } private: FuncType func_; }; // 注册维度取值函数 template<typename R> bool RegisterDimValueFetcher(const std::string& name, const std::function<R(const T&)>& f) { if (!f || fetcher_map_.count(name)) { return false; } fetcher_map_[name] = std::make_unique<Fetcher<R>>(f); priority_list_.emplace_back(name); return true; } // 调整排序优先级(验证维度合法性) bool AdjustPriority(const std::vector<std::string>& priorityList) { for (const auto& name : priorityList) { if (!fetcher_map_.count(name)) { return false; } } priority_list_.assign(priorityList.begin(), priorityList.end()); return true; } // 排序接口:支持任意容器类型 template<typename C> typename std::enable_if<is_container<C>::value, bool>::type sort(C& collection, bool asc = true) { std::sort(collection.begin(), collection.end(), [&](const T& l, const T& r) { for (const auto& dim : priority_list_) { auto iter = fetcher_map_.find(dim); if (iter != fetcher_map_.end()) { bool cmp_result = iter->second->compare(l, r, asc); if (cmp_result) { return true; } else if (iter->second->compare(r, l, asc)) { return false; } // 维度值相等,继续下一个维度 } } return false; // 所有维度相等,保持原有相对顺序 }); return true; } private: std::unordered_map<std::string, std::unique_ptr<FetcherBase>> fetcher_map_; 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, const Student& stu) { os << "{name:" << stu.name << ", age:" << stu.age << "}"; return os; } int main() { 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<Student> gsorter; gsorter.RegisterDimValueFetcher("name", [](const Student& stu) { return stu.name; }); gsorter.RegisterDimValueFetcher("age", [](const Student& stu) { return stu.age; }); // 按name降序排序 gsorter.sort(students, false); for (const auto& stu : students) { std::cout << stu << std::endl; } std::cout << std::endl; // 调整优先级为age升序、name升序 gsorter.AdjustPriority({"age", "name"}); gsorter.sort(students); for (const auto& stu : students) { std::cout << stu << std::endl; } return 0; }
核心改进说明
- 类型擦除实现多类型存储:通过抽象基类
FetcherBase统一接口,用模板子类Fetcher<R>封装不同返回类型的取值函数,容器存储基类智能指针,实现多类型取值器的统一管理。 - 排序逻辑适配多类型:每个取值器自带对应类型的比较逻辑,排序时直接调用
compare方法,无需暴露具体返回类型,解决原代码中模板类型R未定义的问题。 - 细节优化:修复测试代码中
sorter模板参数错误,完善优先级调整的合法性校验,修正输出运算符的常量引用规范。
内容的提问来源于stack exchange,提问作者zhang wei
相关产品推荐
相关产品推荐

