You need to enable JavaScript to run this app. 类C/C++语言类型检查器表达式类型推断的更优雅实现方案咨询
优惠活动
大模型
产品
解决方案
定价
更多

类C/C++语言类型检查器表达式类型推断的更优雅实现方案咨询

更优雅的表达式类型推断实现方案

你当前的实现本质是手动做运行时类型分发,随着表达式节点类型增多会越来越臃肿,扩展性差也容易漏写分支,推荐两种工业界常用的替代方案:

方案1:访问者模式(全C++版本兼容,编译器AST处理标准方案)

BNFC生成的语法节点默认也支持访问者模式接口,你可以直接复用:

实现步骤

  1. 定义表达式访问者抽象基类:
class ExpVisitor {
public:
    virtual Type* visit(ETrue* exp) = 0;
    virtual Type* visit(EFalse* exp) = 0;
    virtual Type* visit(EInt* exp) = 0;
    virtual Type* visit(EDouble* exp) = 0;
    virtual Type* visit(EId* exp) = 0;
    virtual Type* visit(EApp* exp) = 0;
    virtual Type* visit(EUPlus* exp) = 0;
    virtual Type* visit(ExpPlus* exp) = 0;
    virtual Type* visit(ExpAnd* exp) = 0;
    // 其他表达式节点对应visit方法逐一声明
};
  1. 给Exp基类添加纯虚accept方法,所有派生类实现该方法:
class Exp {
public:
    virtual Type* accept(ExpVisitor* visitor) = 0;
    // 其他原有逻辑
};

// 每个表达式派生类实现accept
class ETrue : public Exp {
public:
    Type* accept(ExpVisitor* visitor) override {
        return visitor->visit(this);
    }
    // 其他原有逻辑
};

class EUPlus : public Exp {
public:
    Exp* exp_;
    Type* accept(ExpVisitor* visitor) override {
        return visitor->visit(this);
    }
    // 其他原有逻辑
};
  1. 类型检查器实现访问者接口,把原来每个if分支的逻辑放到对应的visit方法里:
class TypeInferVisitor : public ExpVisitor {
private:
    Env* env_; // 保留原来的环境变量指针
public:
    TypeInferVisitor(Env* env) : env_(env) {}

    Type* visit(ETrue* exp) override {
        return new Type_bool;
    }

    Type* visit(EFalse* exp) override {
        return new Type_bool;
    }

    Type* visit(EInt* exp) override {
        return new Type_int;
    }

    Type* visit(EDouble* exp) override {
        return new Type_double;
    }

    Type* visit(EId* exp) override {
        return env_->lookupVarTypeFromContext(exp->id_);
    }

    Type* visit(EUPlus* exp) override {
        Type* typeExp = exp->exp_->accept(this);
        if(dynamic_cast<Type_int*>(typeExp) || dynamic_cast<Type_double*>(typeExp))
            return typeExp;
        throw TypeException("Trying to unary add wrong type: " + env_->getStringRepresentationOfType(typeExp));
    }

    // 其他节点的visit方法逐一实现
};
  1. 原来的infer方法直接简化为一行:
Type* TypeChecker::infer(Exp* exp) {
    TypeInferVisitor visitor(env_);
    return exp->accept(&visitor);
}

优势

  • 完全消除dynamic_cast,类型分发由编译器静态处理,性能更高
  • 新增表达式节点时编译器会强制要求实现对应的visit方法,不会漏分支
  • 逻辑拆分清晰,每个节点的类型推断逻辑独立,易维护

方案2:std::variant + 重载lambda(C++17及以上适用)

如果你的编译环境支持C++17,不需要修改原有类结构,写法更紧凑:

// 首先定义所有表达式类型的variant
using ExpVariant = std::variant<ETrue*, EFalse*, EInt*, EDouble*, EId*, EApp*, EUPlus*, ExpPlus*, ExpAnd*>;

// 用重载lambda实现分发
Type* TypeChecker::infer(ExpVariant exp) {
    Env* env = env_;
    return std::visit(overloaded{
        [](ETrue* exp) -> Type* { return new Type_bool; },
        [](EFalse* exp) -> Type* { return new Type_bool; },
        [](EInt* exp) -> Type* { return new Type_int; },
        [](EDouble* exp) -> Type* { return new Type_double; },
        [env](EId* exp) -> Type* { return env->lookupVarTypeFromContext(exp->id_); },
        [env, this](EUPlus* exp) -> Type* {
            Type* typeExp = infer(exp->exp_);
            if(dynamic_cast<Type_int*>(typeExp) || dynamic_cast<Type_double*>(typeExp))
                return typeExp;
            throw TypeException("Trying to unary add wrong type: " + env->getStringRepresentationOfType(typeExp));
        },
        // 其他表达式对应的lambda逐一添加
    }, exp);
}

注:overloaded是C++17常用辅助工具,定义如下:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

优势

  • 不需要修改原有Exp类的结构,不需要添加虚函数
  • 代码更紧凑,不需要单独定义访问者类,所有逻辑都在infer函数内

内容的提问来源于stack exchange,提问作者Fo Young Areal Lo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 06:45:04