3

EDIT

The main idea was to make an interface which allow to add new types of elements. For example add Strings, calculating its distance with an 'X' algorithm. That's why i think Templates could not be the correct answer.

I've changed the function's definition of distance in Element class and now its has an implementation.

class Element : public Object
{
...
    virtual float distance(Element *other){return INFINITE;};
...
}

For the Vector class its the same change:

class Vector : public Element
{
...
    virtual float distance(Element *other);
...
}

its implementation is:

float Vector::distance(Element *other)
{   
    if(other->getClass() != this->getClass()) return INFINITE;//this came from Object class
    Vector *n_other = dynamic_cast<Vector*>(other);
    float result = this->L2D(*this,*n_other); 
    return result;
}

OLD

well I'm trying to do an interface in C++ so that the child classes could write these method.

In this case distance represent a value which represent how near(similar) are two elements.

For example I'm trying it now with vectors, but in the future i would use a strings or other things like documents, faces, etc... and it could be used in the same way as an Element.

//Element.h
class Element
{
    virtual float distance(Element const&, Element const&)=0;
};`

then i have the class Vector

//Vector.h
#include "Element.h"
class Vector: public Element
{
    float distance(Vector const&, Vector const&);//(?)
};

And the implementation

#include "Vector.h"
float Vector::distance(Element const &a, Element const &b)
{   
    return Vector::L2D(a,b);//Euclidean distance
}

Well my problem is how can i do this, because i don't find an example for this problem. I'm not pretty sure if you could understand what I'm trying to do... i hope so. Thank u all.

Raziel
  • 444
  • 6
  • 22
  • Child classes dont write methods, they implement them. Why does the distance method take two objects? I would expect it to take either one or be static. – 463035818_is_not_an_ai Nov 26 '15 at 20:04
  • and btw the question is not clear at all (at least to me). How can you do what? You dont find a example for what problem? I dont see any problem. – 463035818_is_not_an_ai Nov 26 '15 at 20:05
  • I think I now slowly start to understand what is the question about ... – 463035818_is_not_an_ai Nov 26 '15 at 20:13
  • I'm not sure if I understand. If the derived class function has no differing code, you might as well just not override the base one. And if it does, then you don't want to call the base one like that. – Weak to Enuma Elish Nov 26 '15 at 20:16
  • 2
    Vector does not implement the virtual method distance. They need to have same signature – Nandu Nov 26 '15 at 20:16
  • Do you maybe want to avoid that it is possible to pass different types of elements to the distance function, but it should only be allowed to pass two `Vectors` or two `Faces` etc.? – 463035818_is_not_an_ai Nov 26 '15 at 20:17
  • Is it intended to calculate distances between objects of different types? Else a (template) function may be more helpful in your case. – Wolf May 13 '16 at 10:32

3 Answers3

2

What you're asking for is called argument covariance. Namely, have the overriding method's argument change with the inheritance change. This intuitively makes sense, but can case type safety issues. Let's assume it were possible, and your code compiled. Then what would have happened here?

Element* e = new Vector();
e->distance(Element(), Element()); // but Vector::distance expects an Element!

This form of overriding is illegal on all conventional languages I'm aware of. For an overriding to be valid, you can't change the arguments' types (you may change the return type sometimes, though).

Your options are therefore:

  1. Stick to the basic signature, and use dynamic_cast to (try to) downcast the arguments on the overriding method
  2. Avoid the pure virtual function, and just have every class define its own method, with the matching arguments types.
  3. Patterns such as double-dispatch may sometimes come handy in such cases.

Note that using templates won't help you here - the virtual function cannot be a template.

Eran
  • 21,632
  • 6
  • 56
  • 89
  • virtual template member functions are not possible but template classes can have virtual functions (see [here](http://stackoverflow.com/a/8919588/4117728)) – 463035818_is_not_an_ai Nov 26 '15 at 20:48
  • @tobi303 sure they can, but the template won't be useful as an interface. – Eran Nov 26 '15 at 20:53
  • well, on one hand I completely agree with you. On the other hand I found it quite interesting that the interface can be used as template parameters as in the example in my answer – 463035818_is_not_an_ai Nov 26 '15 at 20:55
  • @tobi303, what you're suggesting is called CRTP or static polymorphism. Quite a useful pattern, but can't always substitute conventional inheritance. – Eran Nov 26 '15 at 21:05
  • Ah nice, thx for naming it. I was reading about CRTP several times but I never got the point until I rediscovered it ;) – 463035818_is_not_an_ai Nov 26 '15 at 21:16
1

What I can see from your base class Element is that this is an abstract type since at least one of its method's is declared as being purely virtual. This means two things: first, you can not create an object of type Element and second, all inherited classes must implement any functions that are declared as pure virtual.

In your base class you have this as its declaration:

virtual float distance(Element const&, Element const&)=0;

and in your derived class you have this as its declaration:

float distance(Vector const&, Vector const&);//(?)

and in your derived class's implementation you have this:

float Vector::distance(Element const &a, Element const &b) { 
    return Vector::L2D(a,b);//Euclidean distance
}

The base class is stating that this pure virtual function that all derived class have to implement must return a float, and must accept two const references to an Element type. However in your inherited class its declaration is stating otherwise: it is stating that this function does return a float which is not an issue, but its parameter types are since it is being declared as taking two const references to a Vector type. If you are using MS Visual Studio 2012 or newer you can try doing this: add the keyword override after the function declaration in the inherited class so that your inherited declaration would look like this:

float distance(Vector const&, Vector const&) override;

Then try to compile and see if you get any compiler, build or link errors.

The next step would be to change your derived class's declaration and definition to match the base class's declaration of the pure virtual signature. After doing this your derived function declaration would look like this:

float distance(Element const&, Element const&) override;

To understand this what is going on here; here is an example of what I have done to your original code so you can see what is happing with abstract types and inheritance.

Element.h

#ifndef ELEMENT_H
#define ELEMENT_H

class Element {
public: 
    virtual float distance( Element const&, Element const&  ) = 0;    
}; // Element

#endif // ELEMENT_H

Element.cpp

#include "stdafx.h"
#include "Element.h"

// ----------------------------------------------------------------------------
// distance()
float Element::distance( Element const&, Element const& ) { 
    return 0;
} // distance

Vector.h

#ifndef VECTOR_H
#define VECTOR_H

#include "Element.h"

class Vector : public Element {
public:
    float distance( Element const&, Element const& ) override;    
}; // Vector

#endif // VECTOR_H

Vector.cpp

#include "stdafx.h"
#include "Vector.h"

// ----------------------------------------------------------------------------
// distance()
float Vector::distance( Element const&, Element const& ) {
    return 1;
} // distance

main.cpp

#include "stdafx.h"
#include "Vector.h"

int main() {
    float val = 0;

    Vector v1;
    Vector v2;
    Vector v3;
    val = v3.distance( v1, v2 );

    std::cout << val << std::endl;

    std::cout << "Press any key to quit" << std::endl;
    _getch();

    return 0;
} // main

It is hard to tell exactly what you are asking and what you are trying to achieve, but from looking at your code alone, it appears you are using an abstract type using inheritance, but your inherited member function signature is wrong as it does not match the base's pure virtual method's signature.

If you look closely at the small program that I presented to you; this compiles, builds and executes and I am getting the appropriate results.

If you notice here, the declaration is expecting two const& to a type of an Element object, yet when I call the method I am passing in two declared variable types or instances of a Vector object. This works because the Vector objects are inherited from an Element type. Also if you look at the output value, the value printed is 1 and not 0. This is what you want when you are trying to override a base class's function declaration.

Element::distance() returns 0 - This can not be called since it is a pure virtual method causing the base class to be abstract

Vector::distance() returns 1 - This will be called on a vector object.

Maybe this will help you to achieve what you are after.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
0

I am not really sure what is the purpose of the interface, when the child classes methods are supposed to have different signatures. What is possible is this:

#include <iostream>

template <typename T>
struct DistInterf {
    virtual double distance(T other)=0;
};

struct A : DistInterf<A> {
    double distance(A other){
        return 2;
    }
};

int main() {
    A t,t2;
    std::cout << t.distance(t2) << std::endl;
    return 0;
}

However, I dont see any advantage of using the interface here, as each child class will implement a different interface (as I understand the question this is what you are asking for).

I already mentioned in a comment: The distance function should either be static or take only one parameter, otherwise imho it makes little sense.

PS: There is one use case I can think of:

template<typename T> 
void foo(T t1,T t2){
    std::cout << t1.distance(t2) << std::endl;
}

You could pass any type implementing the above interace as template parameter.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185