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

如何通过变通方案实现带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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:55:55