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

如何在C++中实现类似ODB的简洁链式查询功能?

实现ODB风格的C++简洁过滤语法

ODB的简洁查询语法核心是通过重载操作符构建可执行的表达式树,每个表达式节点封装具体的判断逻辑,最终直接用自然的语法完成对象过滤。以下是基于这个思路的完整实现方案,替代你当前繁琐的Specification模式:

核心思路

抛弃虚函数基类的Specification,改用值语义的表达式对象,通过模板和操作符重载,将System::id == 1 && System::model.name == "MyModelName"这类语法转换为可执行的判断逻辑,避免手动创建各类Specification子类的冗余代码。


分步实现

1. 字段代理类:关联对象字段与访问逻辑

先定义一个Field模板类,用来封装对象字段的访问方式(支持直接成员和嵌套成员):

#include <functional>
#include <QList>
#include <QString>

template <typename T, typename FieldType>
struct Field
{
    // 支持任意可调用对象作为字段访问器(直接成员、嵌套成员都能处理)
    using Getter = std::function<const FieldType&(const T&)>;
    Getter getter;

    explicit Field(Getter g) : getter(std::move(g)) {}

    // 重载==操作符,返回相等比较表达式
    auto operator==(const FieldType& val) const
    {
        return EqualExpr<T, FieldType>{*this, val};
    }

    // 可扩展重载>、<、!=、>=、<=等操作符
    auto operator>(const FieldType& val) const
    {
        return GreaterExpr<T, FieldType>{*this, val};
    }
};

2. 表达式节点:封装比较与逻辑运算

每个表达式节点实现eval方法,接收对象实例并返回判断结果,用CRTP避免虚函数开销:

// 基础表达式接口(CRTP)
template <typename Derived, typename T>
struct Expr
{
    bool operator()(const T& obj) const
    {
        return static_cast<const Derived*>(this)->eval(obj);
    }
};

// 相等比较表达式
template <typename T, typename FieldType>
struct EqualExpr : Expr<EqualExpr<T, FieldType>, T>
{
    const Field<T, FieldType>& field;
    FieldType value;

    EqualExpr(const Field<T, FieldType>& f, FieldType v) : field(f), value(std::move(v)) {}

    bool eval(const T& obj) const
    {
        return field.getter(obj) == value;
    }
};

// 大于比较表达式
template <typename T, typename FieldType>
struct GreaterExpr : Expr<GreaterExpr<T, FieldType>, T>
{
    const Field<T, FieldType>& field;
    FieldType value;

    GreaterExpr(const Field<T, FieldType>& f, FieldType v) : field(f), value(std::move(v)) {}

    bool eval(const T& obj) const
    {
        return field.getter(obj) > value;
    }
};

// 逻辑与表达式
template <typename T, typename LhsExpr, typename RhsExpr>
struct AndExpr : Expr<AndExpr<T, LhsExpr, RhsExpr>, T>
{
    LhsExpr lhs;
    RhsExpr rhs;

    AndExpr(LhsExpr l, RhsExpr r) : lhs(std::move(l)), rhs(std::move(r)) {}

    bool eval(const T& obj) const
    {
        return lhs(obj) && rhs(obj);
    }
};

// 全局重载&&操作符,用于拼接两个表达式
template <typename T, typename Lhs, typename Rhs>
auto operator&&(const Expr<Lhs, T>& lhs, const Expr<Rhs, T>& rhs)
{
    return AndExpr<T, Lhs, Rhs>{static_cast<const Lhs&>(lhs), static_cast<const Rhs&>(rhs)};
}

3. 为目标类定义字段代理

修改Model和System类,添加getter方法并定义静态字段代理(支持嵌套字段):

class Model
{
    int id;
    QString name;
public:
    int getId() const { return id; }
    const QString& getName() const { return name; }

    // 静态字段代理
    static inline const Field<Model, int> id{&Model::getId};
    static inline const Field<Model, QString> name{&Model::getName};
};

class System
{
    int id;
    int user_id;
    Model model;
public:
    int getId() const { return id; }
    int getUserId() const { return user_id; }
    const Model& getModel() const { return model; }

    // 静态字段代理
    static inline const Field<System, int> id{&System::getId};
    static inline const Field<System, int> user_id{&System::getUserId};
    // 嵌套字段:用lambda封装访问逻辑
    static inline const Field<System, QString> model_name{
        [](const System& s) -> const QString& { return s.getModel().getName(); }
    };
};

4. Query类与Filter适配

Query类直接封装表达式对象,Filter接收Query完成过滤:

template <typename T, typename ExprType>
struct Query
{
    ExprType expr;

    explicit Query(ExprType e) : expr(std::move(e)) {}

    bool matches(const T& obj) const
    {
        return expr(obj);
    }
};

template <typename T>
struct Filter
{
    QList<T*> filter(QList<T*> items, const auto& query)
    {
        QList<T*> result;
        for (auto item : items)
        {
            if (query.matches(*item))
            {
                result.push_back(item);
            }
        }
        return result;
    }
};

最终使用方式

现在可以写出你期望的简洁代码:

// 假设已初始化的System实例列表
QList<System*> system_list;

Filter<System> system_filter;
// 直接用自然语法构建查询
auto system_query = Query<System>(System::id == 1 && System::model_name == "MyModelName");
auto filtered_list = system_filter.filter(system_list, system_query);

扩展方向

  • 支持逻辑或||、逻辑非!操作符,实现方式参考AndExpr
  • 添加正则匹配表达式,重载operator~实现模糊查询
  • 支持C++17类模板参数推导,简化Query声明为auto query = Query{...}

内容的提问来源于stack exchange,提问作者Mario de la Rosa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:26:25