简体   繁体   中英

C++ virtual function override

I have a class that contains the following virtual method:

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

The above doesn't work because:

error: templates may not be ‘virtual’

The plan is to specialize the class by making more specific instances of it like point2D , point3D . However, I only want the function to work with types of the same class. So if point2D where to inherit this class, the method distTo should only take parameter of type point2D . How can I accomplish this?

This is what I had tried before I did the above:

virtual double distTo(point& other) = 0;

But when I override this method in the point2D class and try to replace the parameter with one of type point2D , I run into compiler errors.

Thanks for your time

This sounds like the Curiously Recurring Template Pattern. Furthermore, this is completely incompatible with dynamic indirection, as the compiler cannot statically verify the dynamic type (obviously). But the CRTP can only be used to implement the function, not declare it.

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

Fundamentally, the interface you are trying to declare is completely impossible because you're asking the compiler to statically typecheck dynamic types. There is no solution that offers all the properties you want.

I think your requirements make no sense for a static typed language such as C++.

Think about how would you be able to use your virtual function:

point2d p1, p2;
point3d p3;

point &p = p1;

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

That is simply not possible, because at compile time the compiler will not know if p is a reference to a point2d or a point3d , only at runtime.

You could add an explicit cast and a runtime assertion if you do it wrong, but I think that it make little sense. Simply do:

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

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

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

And do not call distTo using base point references.

UPDATE : If you know that your list is homogeneous, but you don't know the cardinality, then you can do:

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);
    }
};

But beware! If you call point::distTo with a wrong object, the result will be undefined!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM