如何通过变通方案实现带Vector2概念约束的纯虚contains函数?
让带Concept约束的成员函数成为纯虚函数的变通方案
问题描述
现有如下Concept和类成员函数模板:
template<typename T> concept Vector2 = requires (T t) { t.x; t.y; }; struct Shape { bool contains(const Vector2 auto&) { ... } };
希望让contains成为纯虚函数,使得子类(如Circle、Polygon)能按类似方式继承并重写,但C++不允许虚函数模板,且需兼容不同库的Vector2类型(通过Vector2 Concept约束),需寻找可行的变通方案。
可行方案
方案1:非虚接口(NVI) + 类型擦除桥接层
核心思路是将对外暴露的模板函数作为非虚接口,内部调用纯虚桥接函数,通过类型擦除统一不同Vector2类型的访问方式。
步骤1:定义Vector抽象包装类
创建抽象类封装Vector2属性的访问,再用模板类适配所有符合Concept的Vector2类型:
class VectorWrapper { public: virtual ~VectorWrapper() = default; virtual float getX() const = 0; virtual float getY() const = 0; }; template<Vector2 V> class ConcreteVectorWrapper : public VectorWrapper { public: ConcreteVectorWrapper(const V& vec) : vec_(vec) {} float getX() const override { return vec_.x; } float getY() const override { return vec_.y; } private: const V& vec_; };
步骤2:改造Shape类
将模板contains作为非虚public接口,内部调用纯虚桥接函数do_contains:
struct Shape { // 对外暴露的模板接口,符合Vector2约束的类型均可传入 template<Vector2 V> bool contains(const V& vec) const { ConcreteVectorWrapper<V> wrapper(vec); return do_contains(wrapper); } // 纯虚桥接函数,子类必须重写 virtual bool do_contains(const VectorWrapper&) const = 0; };
步骤3:子类实现
子类只需重写do_contains,通过VectorWrapper获取坐标并实现判断逻辑:
struct Circle : public Shape { // 假设Circle有具体的中心坐标和半径 LibAVector2 center; float radius; bool do_contains(const VectorWrapper& wrapper) const override { float x = wrapper.getX(); float y = wrapper.getY(); float dx = x - center.x; float dy = y - center.y; return dx*dx + dy*dy <= radius*radius; } };
方案2:Visitor模式适配
若需更灵活的类型扩展,可采用Visitor模式,将不同Vector2类型的处理逻辑分发到对应访问器中。
步骤1:定义Vector-Shape访问器
针对每个Vector2类型,定义访问不同Shape子类的接口:
template<Vector2 V> struct VectorShapeVisitor { const V& vec; VectorShapeVisitor(const V& v) : vec(v) {} virtual bool visit(const Circle&) const = 0; virtual bool visit(const Polygon&) const = 0; // 新增Shape子类时需扩展此处 };
步骤2:改造Shape类
添加模板化的accept纯虚函数,对外暴露模板contains接口:
struct Shape { template<Vector2 V> bool contains(const V& vec) const { // 针对当前Vector类型创建具体访问器 struct ConcreteVisitor : VectorShapeVisitor<V> { using VectorShapeVisitor<V>::vec; bool visit(const Circle& circle) const override { return circle.contains(vec); } bool visit(const Polygon& poly) const override { return poly.contains(vec); } }; ConcreteVisitor visitor(vec); return accept(visitor); } template<Vector2 V> virtual bool accept(VectorShapeVisitor<V>&) const = 0; };
步骤3:子类实现
子类重写accept函数,并实现自身的contains模板逻辑:
struct Circle : public Shape { LibAVector2 center; float radius; // 直接处理Vector2类型的contains逻辑 template<Vector2 V> bool contains(const V& vec) const { float dx = vec.x - center.x; float dy = vec.y - center.y; return dx*dx + dy*dy <= radius*radius; } template<Vector2 V> bool accept(VectorShapeVisitor<V>& visitor) const override { return visitor.visit(*this); } };
说明
两种方案均满足需求:对外提供兼容任意Vector2类型的模板接口,同时通过虚函数实现子类的多态重写。方案1实现简洁,适合大多数场景;方案2更灵活,适合需要频繁扩展Shape或Vector类型的场景。
内容的提问来源于stack exchange,提问作者JensB
相关产品推荐
相关产品推荐

