【问题标题】:C++ virtual function overrideC++ 虚函数覆盖
【发布时间】:2015-06-17 08:24:52
【问题描述】:

我有一个包含以下虚方法的类:

struct point {
    template<typename T>
    virtual typename std::enable_if<std::is_base_of<point, T>::value, double>::type distTo(T &other) const = 0;
};

上述方法不起作用,因为:

error: templates may not be ‘virtual’

计划是通过制作更具体的类实例来专门化该类,例如point2Dpoint3D。但是,我只希望该函数与同一类的类型一起使用。所以如果point2D从哪里继承这个类,方法distTo应该只接受point2D类型的参数。我怎样才能做到这一点?

这是我在执行上述操作之前尝试过的:

virtual double distTo(point& other) = 0;

但是当我在 point2D 类中重写此方法并尝试将参数替换为 point2D 类型之一时,我遇到了编译器错误。

感谢您的宝贵时间

【问题讨论】:

  • 一个虚函数不能比它的包含类更多的模板。但是如果包含类是模板,那么它的虚函数也可以是模板。另一方面,派生类可以比其基类更多模板,并提供其虚函数的模板实现......

标签: c++ inheritance polymorphism abstract-class


【解决方案1】:

我认为您的要求对于静态类型语言(例如 C++)没有任何意义。

想想你将如何使用你的虚函数:

point2d p1, p2;
point3d p3;

point &p = p1;

p.distTo(p2); //ok?
p.distTo(p3); //error?

这根本不可能,因为在编译时,编译器仅在运行时才知道p 是对point2d 还是point3d 的引用。

如果你做错了,你可以添加一个显式的强制转换和一个运行时断言,但我认为这没什么意义。只需这样做:

struct point { /*...*/ };

struct point2d : point {
    double distTo(const point2d &other);
};

struct point3d : point {
    double distTo(const point3d &other);
};

并且不要使用基本的point 引用来调用distTo

更新:如果你知道你的列表是齐次的,但你不知道基数,那么你可以这样做:

struct point {  
    virtual double distTo(const point &other) =0;
};

struct point2d : point {
    double distTo(const point2d &other) { /*...*/ }
    virtual double distTo(const point &other) {
        const point2d &other2 = static_cast<const point2d &>(other);
        return distTo(other2);
    }
};

struct point3d : point {
    double distTo(const point3d &other) { /*...*/ }
    virtual double distTo(const point &other) {
        const point3d &other3 = static_cast<const point3d &>(other);
        return distTo(other3);
    }
};

但要小心!如果你用错误的对象调用point::distTo,结果将是未定义的!

【讨论】:

  • 由于多态性,我可以在一个列表中有许多这样的点,并且都具有point 类型,那么这个解决方案将不起作用
  • @Smac89:没有维护类型安全和动态间接的解决方案。
  • @Smac89:但是如果您有一个对point 的引用列表,您会如何处理它们?调用point::distTo(),你会向函数传递什么,point2d
  • 不,会检查列表以确保列表中的所有点具有相同的基数,因此您仍然可以为其中任何一对调用distoTo。我知道编译器无法知道这一点
  • 如果列表中的所有项目实际上都是point2d,那么只需将它们转换为point2d&amp; 并调用非虚拟point2d::distTo()。如果您不知道基数但确定它们是相同的,请查看我的更新答案。
【解决方案2】:

这听起来像是奇怪重复的模板模式。此外,这与动态间接完全不兼容,因为编译器无法静态验证动态类型(显然)。但是CRTP只能用于实现函数,不能声明。

template<typename T> class Point {
public:
    double distTo(T other) {
        /* stuff */ 
    }
};
class Point2D : public Point<Point2D> {
    // distTo automatically defined
};

基本上,您尝试声明的接口是完全不可能的,因为您要求编译器对动态类型进行静态类型检查。没有提供您想要的所有属性的解决方案。

【讨论】:

  • 完全明白非常感谢。我只是在想同样的事情,但使用 java - Comparator 接口
  • @Smac89:实现涉及手动错误类型的对象。编译器无法捕获非泛型版本的类型错误,泛型版本无法以您想要的方式存储在列表中。
  • 但是,Point2DPoint3D 将没有共同的基类,这可能与 OP 无关或非常重要,具体取决于它们的代码库。
  • @rodrigo:拥有基类不能提供他想要的接口(至少对于这个函数来说),所以它或多或少无关紧要。
猜你喜欢
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 2014-05-22
  • 2013-10-04
  • 2010-10-04
  • 2013-01-16
  • 2021-09-30
  • 2015-12-15
相关资源
最近更新 更多