1

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

smac89
  • 39,374
  • 15
  • 132
  • 179
  • A virtual function can't be more template than its containing class. But if the containing class is template, so can be its virtual functions. On the other hand, a derived class CAN be more template than its base class, and provide a template implementation of its virtual functions... – Medinoc Apr 11 '15 at 18:23

2 Answers2

3

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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Completely understand thanks very much. I was just thinking about the same thing but with java - the `Comparator` interface – smac89 Apr 11 '15 at 18:25
  • @Smac89: Implementing that involves manually erroring on objects of the wrong type. The compiler cannot catch type errors on the non-generic version, and the generic version cannot be stored in a list in the way you want. – Puppy Apr 11 '15 at 18:26
  • But then, `Point2D` and `Point3D` will not have a common base class, which may be irrelevant to the OP or very important, depending on their code base. – rodrigo Apr 11 '15 at 18:38
  • @rodrigo: Having the base class can't offer the interface he wants (at least for this function) so it's more-or-less immaterial. – Puppy Apr 11 '15 at 18:49
1

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!

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Because of polymorphism, I could have a number of these points in a list and all with the type `point`, then this solution will not work – smac89 Apr 11 '15 at 18:23
  • @Smac89: There is no solution that maintains type safety and dynamic indirection. – Puppy Apr 11 '15 at 18:23
  • @Smac89: But if you have a list references to `point`, what would you do with them? Call `point::distTo()` what would you pass to the function, a `point2d`? – rodrigo Apr 11 '15 at 18:24
  • No, the list would have been checked to make sure that all the points in the list have the same cardinality, so that you can still call `distoTo` for any pair of them. I understand the compiler cannot know this – smac89 Apr 11 '15 at 18:26
  • If all the items in the list are actually `point2d`, then simply cast them to `point2d&` and call a non-virtual `point2d::distTo()`. If you don't know the cardinality but you are sure they are the same, then see my updated answer. – rodrigo Apr 11 '15 at 18:28
  • The idea for casting brought me to [this](http://stackoverflow.com/a/11510210/2089675) – smac89 Apr 11 '15 at 18:53