-1

I want to create interface like

class Scalar {
public:
  Scalar() {}
  virtual ~Scalar() {}
  //virtual members operators
  virtual Scalar& operator+() const = 0;
  virtual const Scalar operator-() const;
  virtual Scalar& operator=() = 0;
  virtual Scalar& operator+=() = 0;
  //...
};

I intend also to use some friend functions, for example:

    friend const Scalar operator+(const Scalar&, const Scalar&);

But there is a problem when I derive the abstract class, and create derived class, say:

class RealNumber: public Scalar {
public:
  friend const RealNumber operator+(const RealNumber&, const RealNumber&);
  //some definitions...
};

According to this logic, I would need to define a new overload of friend operator+ for every new class derived from Scalar. Is there some way to solve this problem and avoid declaring these friends in all the derived classes?

Christophe
  • 68,716
  • 7
  • 72
  • 138
Maciej
  • 145
  • 5
  • 1
    this is an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) . – bolov Oct 06 '18 at 16:20
  • If using a friend function in a derived class, you will also have access to protected members which are inherited from the base class. But this does only work, if the static type of the accessing variable is from (in this case) Y and not a polymorph variable of X (like in your case). – birdfreeyahoo Oct 06 '18 at 16:22
  • 1
    You usually don't want to overload operators of types in hierarchies in the first place. Those designs have a strong tendency to break down in practice. Lack of multiple dispatch is just one of the reasons for that. – Sebastian Redl Oct 06 '18 at 16:49
  • Why friend function?? you can make a virtual + operator. BUT THIS IS NOT THE BEST WAY – user9335240 Oct 06 '18 at 16:57
  • I think you will find that this approach will be fraught with bugs and problems. And it isn't just C++, but in every OOP language with which I'm familiar. – Eljay Oct 06 '18 at 19:54
  • I've slightly reworded your question, especially the end, in order to clarify what exactly is the problem. Don't hesitate to edit if I misunderstood something. – Christophe Oct 06 '18 at 21:56

2 Answers2

3

Is this your problem ?

I understand that your problem is that your two friends refer to totally different functions, since they have a different signature:

friend const Scalar operator+(const Scalar&, const Scalar&);
friend const RealNumber operator+(const RealNumber&, const RealNumber&);

Worse, the choice of a class external friend will not be polymorphic: the right friend will be chose based on the compile-time type.

How to solve it ?

First of all, instead of using an outside overloaded friend, you could consider overriding the operator of the class itself (keeping the signature identical).

However this has two major challenges:

  • it is almost impossible to return a reference from arithmetic operator, unless you'd accept side-effects, which would then make your operator behave differently than expected.
  • you'd need to cope with combining different kind of scalars: what if you'd have to add a Scalar to a RealNumber ? This would require a double dispatch to be implemented to cope with all the possible combination.

So, dead-end ?

No, there are two other alternatives, depending on your real problem:

  • Do you really want to combine arithmetic type dynamically at run-time ? If yes, you need to go away from the C++ operator overriding approach, and implement an expression evaluator, using the interpreter pattern.
  • If not, consider to use a template based design, so that the compiler choses at compile-time the appropriate specialisation.
  • Or suffer with the many possible friends and their combination of parameter types.
Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
1

You may not be able to create virtual friend functions, but you can create virtual operators (even operator + could be done this way).

Consider the following code: WARNING: THIS IS NOT GOOD DESIGN AT ALL

#include <iostream>
using namespace std;

class AA {
private:
    int a;
public:
    AA(int a): a(a) {};
    inline int getA() const { return a; };
    virtual AA operator +(const AA &a) {
        AA res(this->getA() + a.getA());
        return res;
    }
};

class BB: public AA {
    public:
    BB(int a): AA(a) {}
    virtual AA operator +(const AA &a) {
        AA res(this->getA() - a.getA());
        return res;
    }
};

int main() {
    BB tmp(1);
    AA& a = tmp;
    AA b(7);
    cout << (a + b).getA();
    return 0;
}

When I was writing this code, I found that a lot of flaws could be induced (like the + operator that really does substraction instead, also what if the second operand was BB instead of the first one ??)

So, about your problem, you want Scalars. So you can do the following approach:

#include <iostream>
using namespace std;

// Here, Scalar acts as an abstract class, all its goal is to convert your
// class type into some calculable value type (e.g. you can use T as double)
template <typename T>
class Scalar {
public:
    // Converter function is abstract
    virtual operator T() = 0;
};

class AA: public Scalar<double> {
private:
    double a;
public:
    inline double getA() {return a;};
    AA(double a): a(a) {}
    // Implements the converter function in Scalar, T in scalar is mapped
    //    to double because we did Scalar<double>
    virtual operator double() {
        return a;
    }
};

class BB: public Scalar<double> {
private:
    int a;
public:
    inline double getA() {return (double)a;};
    BB(int a): a(a) {}
    virtual operator double() {
        return (double)a;
    }
};

int main() {
    BB tmp(1);
    AA b(7);
    // Here, it is easy for us just to add those, they are automatically converted into doubles, each one like how it is programmed.
    cout << (b + tmp);
    return 0;
}
user9335240
  • 1,739
  • 1
  • 7
  • 14
  • 1
    Thank you, but it doesn't solve my problem. What I want is to have finite fields to operate with, so it is very important to override operator+ and operator*. I discover, that I can partially specialize templates, it is very helpful(and it can use late-binding, if you do it smart!). Thank you for your answer :) – Maciej Oct 06 '18 at 20:04
  • @Maciej And really templates are better, because `virtual` functions are dynamic dispatch, which is something old school and bound to something like Java/Objective-C and other slow languages, while template is about static compile time resolution, which is like Swift and Rust, the modern languages – user9335240 Oct 06 '18 at 20:15
  • Thanks for the hint :) – Maciej Oct 08 '18 at 13:46