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

使用类型擦除扩展结构体以避免大规模重构的问题咨询

问题与解答

问题背景

现有代码里广泛用Path(定义为using Path = std::vector<Point>;),Point只存数据,没额外功能。其中有个过滤方法:

Path filter(const Path& path) {... return filteredPath; }

现在需要给点加额外描述信息,打算做PointExt : Point这类扩展类型,但直接继承得把Path改成std::vector<std::unique_ptr<Point>>,要改大量代码。所以想搞类型擦除方案,既能保留原来加Point的用法,也能加PointExt:

Path path;
path.push_back(Point());
path.push_back(PointExt());

把Point改成类型擦除结构、定义PointExt后,出现attempting to reference a deleted function错误,想问两个问题:

  1. 错在哪了?
  2. 这个解决方案合理吗?

相关代码

struct Point
{
    template<typename T>
    Point(T&& p): object(std::make_shared<Model<T>>(std::forward<T>(p))) {}

    virtual PointType getType() const { return object->getType(); }
  
    struct Concept
    {
        virtual ~Concept() {}
        virtual PointType getType() const = 0;
    };

    template<typename T>
    struct Model : Concept
    {
        Model(const T& t) : object(t) {}
        T object;
        PointType getType() const override { return object.getType(); }
    };

    int x;
    int y;
    std::shared_ptr<Concept> object;
};

struct PointExt : Point
{
   PointType getType() const override { return PointType::PointExt ; }
    
   std::vector<Solution> solutions;
};

struct PointExt2 : Point
{
   PointType getType() const override { return PointType::PointExt2 ; }
    
   ...
};

更新说明

已经找到错误原因:Point类缺默认构造函数,加Point() = default;后错误修复。


解答

1. 错误原因分析

你碰到的attempting to reference a deleted function错误,核心是自定义构造函数后,编译器不会自动生成默认构造函数。PointExt继承Point,创建PointExt对象时会先调用Point的默认构造函数初始化基类部分,但你的Point只定义了模板构造函数,默认构造函数被编译器自动删除,导致基类初始化失败,触发错误。加Point() = default;显式声明默认构造函数后,编译器会生成该函数,问题就解决了。

2. 解决方案合理性分析

这个类型擦除的思路有可取之处,但也有不少需要注意的问题:

  • 优点:
    • 不用大规模修改现有Path的定义,保留了原有代码的兼容性,不用把容器改成智能指针类型,改动成本低。
    • 能灵活扩展不同的Point子类,在需要额外信息的地方通过getType()判断类型后转换,满足业务需求。
  • 潜在问题:
    • 类型转换风险:后续要拿PointExt等子类的额外信息时,必须先通过getType()判断类型,再用dynamic_cast转换,一旦类型判断错或者转换失败,会引发运行时错误,增加维护成本。
    • 数据冗余:你的Point本身有int x; int y;成员,而类型擦除的Model又持有一个T对象(比如PointExt继承Point,会包含基类的x、y和自身的solutions),这会造成数据重复,浪费内存。
    • 设计混淆:原本的Point是数据存储类,现在被改成类型擦除的容器类,同时又当基类让其他类继承,职责变得模糊,不符合单一职责原则。

优化建议

如果要继续用类型擦除方案,可以调整设计:

  1. 让Point只做接口或数据载体,把类型擦除的逻辑抽离到单独的类(比如AnyPoint)里,Path改成std::vector<AnyPoint>,虽然要改部分现有代码,但改动范围比换成智能指针小。
  2. 删掉Point里的x、y成员,让所有具体点类型(包括原Point)都实现包含x、y的接口,通过类型擦除的Concept提供访问x、y的方法,避免数据冗余。
  3. 如果现有代码改动限制很严,也可以保留当前方案,但要严格规范类型转换的使用,尽量把转换逻辑封装起来,别在多处直接用dynamic_cast。

内容的提问来源于stack exchange,提问作者theateist

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 08:25:24