4

I have a parent class Obj with empty virtual function cmp

class Obj{
 public:
   virtual int cmp(const Obj& val) = 0;
   ...
};

I am trying to define that function in the subclass MyString, but instead of const Obj& as argument, i use const MyString& which probably occures the error "Emprty virtual function Obj::cmp has no redifinition"

class MyString : public Obj{
private:
    ...
public:
    virtual int cmp(const MyString& val){
        ... using private values of MyString
    }
};

So how can i solve that, if i have 3-4 subclasses which uses their own variables in that function

nurettin
  • 11,090
  • 5
  • 65
  • 85
RomaFUN
  • 49
  • 6
  • 2
    You want to overload not override. Please check this one for the answer: https://stackoverflow.com/questions/30638634/virtual-operator-overloading-c?rq=1 – Luan Pham Dec 09 '20 at 08:17
  • 2
    @LuanPham is right. If you want to change the arguments, that is called an overload. The error message you are getting is because you don't define the purely virtual function of your base class in MyString. – FloWil Dec 09 '20 at 08:30
  • This is known as a *binary method problem*, look it up. The object-oriented paradigm has no satisfactory solution for it that works with static typing. The fundamental error is having `Obj` (or at least the `cmp` method in `Obj`) in the first place. – n. m. could be an AI Dec 09 '20 at 08:43
  • C++ doesn't support contravariance, and it should be the other way anyway, `Base::foo(Derived&)` and `Derived::foo(Base&)`. – Jarod42 Dec 09 '20 at 11:12

2 Answers2

3

An example that comes to my mind is the curiously recurring template pattern. It goes like this:

#include <iostream>
#include <ostream>

template <typename T>
struct Base
{
    virtual int cmp(T const &) = 0;
};

struct First : Base<First>
{
    virtual int cmp(First const &);
};

int First::cmp(First const &)
{
    std::cout << "First\n";
    return 1;
}

struct Second : Base<Second>
{
    virtual int cmp(Second const &);
};

int Second::cmp(Second const &)
{
    std::cout << "Second\n";
    return 2;
}

int main()
{
    Base<First>* f1 = new First();
    Base<First>* f2 = new First();
    Base<Second>* s = new Second();
    f1->cmp(*dynamic_cast<First*>(f2)); // if this will throw, use RAII instead of deletes below
    // f1->cmp(*dynamic_cast<Second*>(f2)); error: cannot convert Second to First
    delete f1;
    delete f2;
}
nurettin
  • 11,090
  • 5
  • 65
  • 85
  • 2
    With this solution there is no need to have `cmp` as a virtual method in `Base`. Consequently, if the only purpose of `Base` is to provide `cmp`, then there is no need to have `Base` at all. You might just as well have `First` and `Second` as independent classes not inheriting from anything. – n. m. could be an AI Dec 09 '20 at 08:46
  • @n.'pronouns'm. I interpret the "..." in the question as there being more to the base class – Andreas Dec 09 '20 at 09:02
2

If you want to use overriding you have to define method in subclasses as it is in base class and can't change parameters of virtual function. In method of subclass you can cast to a type you need.

class Obj
{
    public:
        virtual int cmp(const Obj& val) = 0;
        ...
};

class MyString : public Obj
{
    private:
        ...
    public:
        int cmp(const Obj& val)
        {
            // cast val to MyString& (you can use dynamic_cast)
            ... using private values of MyString
        }
};

Also you can use overloading. In this case you don't need virtual method in base class.

class Obj
{
    public:
        int cmp(const Obj& val)
        {
             // implementation for Obj
        }
        ...
};

class MyString : public Obj
{
    private:
        ...
    public:
        int cmp(const MyString& val)
        {
            ... using private values of MyString
        }
};
Gor Asatryan
  • 904
  • 7
  • 24
  • This is all wrong. The cast defeats the purpose of strong typing. The terminology is backwards: the first example is overriding and the second one is overloading. You also probably want a different signature in the second one. – n. m. could be an AI Dec 09 '20 at 08:38
  • I just used a dynamic_cast as mentioned in the first example and it worked for me – RomaFUN Dec 09 '20 at 08:45
  • 2
    @RomaFUN Of course it works when you pass the correct type to `cmp`. However the purpose of static typing is to raise a **compile-time** error it you try to pass an incorrect type around. Having a `dynamic_cast` means there will be no longer a compile-time error, but a run-time error instead. It's your decision if you are OK with that. – n. m. could be an AI Dec 09 '20 at 08:50
  • @n.'pronouns'm. sorry for mistake. already fixed – Gor Asatryan Dec 09 '20 at 08:55
  • @n.'pronouns'm. If one sets up the abstract base class / derived class pattern to allow polymorphism, then surely one of the main benefits is that the code does not need to know the concrete type of MyString at compile time, only that it implements Obj. So I would have thought that run-time error-checking comes with the territory? – DS_London Dec 09 '20 at 09:11
  • @DS_London No, you normally don't have to insert run-time type checks and rasie run-time type errors in every method whenever inheritance involved. That's the promise of static typing. It is important to be able to trust your function signatures. `cmp (const Obj&)` means **any** kind of `Obj` is OK here. Classes derived from `Obj` violate that promise. If you cannot rely on advertised types, what's the purpose of having static types in the first place? – n. m. could be an AI Dec 09 '20 at 09:30
  • @n.'pronouns'm. Sorry I wasn't being clear. If you wanted a polymorphic cmp(const Obj& val), so that you could hold a dynamic list of Obj (for example), and compare the instances at run-time. The static type checking is working in the sense that it insists that the derived class parameter has a cmp() implementation with the correct signature: it is not guaranteeing that the parameter will do what you want when you call cmp(). Maybe I am reading between the lines too much on the questioner's intentions. – DS_London Dec 09 '20 at 11:23
  • @DS_London "If you wanted a polymorphic cmp(const Obj& val)" My point is that wanting this thing is not useful. "it is not guaranteeing that the parameter will do what you want" I want no run-time type errors. This is why I have static types in the first place. If static types cannot guarantee me that, I'm better off without them. Normally they stand in my way (justifiably) when I attempt to write a type-incorrect program. Thus they restrict me but provide a benefit in return: no type errors. If I can write a type-incorrect program anyway, then they just restrict me, giving nothing in return. – n. m. could be an AI Dec 09 '20 at 12:58