You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

核心改进说明

  1. 类型擦除实现多类型存储:通过抽象基类FetcherBase统一接口,用模板子类Fetcher<R>封装不同返回类型的取值函数,容器存储基类智能指针,实现多类型取值器的统一管理。
  2. 排序逻辑适配多类型:每个取值器自带对应类型的比较逻辑,排序时直接调用compare方法,无需暴露具体返回类型,解决原代码中模板类型R未定义的问题。
  3. 细节优化:修复测试代码中sorter模板参数错误,完善优先级调整的合法性校验,修正输出运算符的常量引用规范。

内容的提问来源于stack exchange,提问作者zhang wei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 14:47:15