类C/C++语言类型检查器表达式类型推断的更优雅实现方案咨询
更优雅的表达式类型推断实现方案
你当前的实现本质是手动做运行时类型分发,随着表达式节点类型增多会越来越臃肿,扩展性差也容易漏写分支,推荐两种工业界常用的替代方案:
方案1:访问者模式(全C++版本兼容,编译器AST处理标准方案)
BNFC生成的语法节点默认也支持访问者模式接口,你可以直接复用:
实现步骤
- 定义表达式访问者抽象基类:
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方法逐一声明 };
- 给
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); } // 其他原有逻辑 };
- 类型检查器实现访问者接口,把原来每个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方法逐一实现 };
- 原来的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
相关产品推荐
相关产品推荐

