1

Suppose we have the following class:

class Rational { // Represents rational number. n=1/2=d for example.
public:
  int n = 0;
  int d = 1;
};

Rational x = Rational();
x.n = 1;
x.d = 2;

Is it possible to do overloading such that 3 * x would give 3/2 instead of an error?

My teacher said that overloading happens only between objects of the same type, but why can we do overloading between cout which is of type ostream and an object of type Rational and not of the type int and Rational?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Shinobi San
  • 143
  • 4
  • 2
    You need to implement arithmetic operators for your class. See here (look for the "arithmetic" section(s)): https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading. – wohlstad May 17 '22 at 16:17
  • If you implement the `operator` functions, then you can do `3 * x` and it would do `3 * n / d` – 2pichar May 17 '22 at 16:17
  • Re: overloading happens only between objects of the same type. Overloading happens only in functions, and it by definition means taking different number or types or arguments. You surely can overload operator `*` where it takes your class and an integer value – The Dreams Wind May 17 '22 at 16:18
  • 2
    *"My teacher said that overloading happens only between objects of the same type"*. Your teacher is wrong. – Jarod42 May 17 '22 at 16:19
  • 1
    "My teacher said that overloading happens only between objects of the same type" Either thats not what they said or theyre wrong, also `Rational& operator*(Rational& lhs, float rhs) {lhs.n *= rhs; return lhs; }` seems to do what is asked. – Borgleader May 17 '22 at 16:19
  • 1
    That looks like a pretty weird `operator*` if it modifies one of its arguments. I'd expect `operator*` to return a `Rational` and `operator*=` to mutate the left hand side and return a `Rational&`. – Nathan Pierson May 17 '22 at 16:22
  • Indeed. `*` should normally take a `const Rational &`. Although I can see a benefit of also having `Rational operator *(Rational &&lhs, float rhs)` for e.g. a large matrix. – Goswin von Brederlow May 17 '22 at 17:17

1 Answers1

2

You may write for example

Rational operator *( const Rational &r, int x )
{
    return { r.n * x, r.d };
}

Rational operator *( int x, const Rational &r )
{
    return { r.n * x, r.d };
}

You may overload operators for user defined types. For a binary operator at least one of operands must be of a user defined type.

From the C++ 20 Standard (12.4.2.3 Operators in expressions)

2 If either operand has a type that is a class or an enumeration, a user-defined operator function can be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. Therefore, the operator notation is first transformed to the equivalent function-call notation as summarized in Table 15 (where @ denotes one of the operators covered in the specified subclause). However, the operands are sequenced in the order prescribed for the built-in operator (7.6).

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thank you. Do I have to write two functions for the case where ```3*x``` and the case ```x*3``` or will one function be enough. I just tried it and it seems like one function is enough, but it's always good to ask. – Shinobi San May 17 '22 at 16:27
  • 2
    @ShinobiSan As the operator is commutative then you should to define the both operators. – Vlad from Moscow May 17 '22 at 16:30
  • I didn't really understand, if it is commutative then one function should be enough right? – Shinobi San May 17 '22 at 16:33
  • 1
    @ShinobiSan Not enough. That is if you will overload the operator for the expression 3 * x then for the expression x * 3 the compiler will issue an error because there is no such an operator for this order of arguments of these types. – Vlad from Moscow May 17 '22 at 16:35
  • 1
    C++ overload resolution doesn't assume that `*` is commutative. (It isn't, always.) So if you have `operator*(const Rational&, int)` but not `operator*(int, const Rational&)` and you try to write `3 * someRational`, the compiler will not attempt to go "I don't know what this means, but would I be able to do something if I tried `someRational * 3` instead?" – Nathan Pierson May 17 '22 at 16:36
  • 1
    @ShinobiSan One could (and should) implement the function logic only once, and refer to this one implementation also in the second function wwhich applies for interchanged parameters. – davidhigh May 17 '22 at 16:43
  • 2
    Yeah I'd suggest that in Vlad's answer the second function should just read `return r * x;` – Nathan Pierson May 17 '22 at 16:45