How to determine the return value when operators are overloaded? In which case should the object reference be returned, and in which bitwise copy of it?

update:

#include <iostream> using namespace std; class SomeClass { int value; public: SomeClass(int value) { this->value = value; } SomeClass& operator=(const SomeClass &rhs) { value = rhs.value; return *this; } SomeClass operator+(const SomeClass &rhs) { value += rhs.value; return *this; } }; int main(int argc, char *argv[]) { SomeClass a(1), b(2); a = a + b; return 0; } 

2 answers 2

Operators are of two types: operators with side effects that modify one of their operands (increment / decrement, simple assignment, compound assignments like += , etc.), and operators without side effects that do not modify their operands, but return result in a new facility.

It is here that usually passes the "watershed" on the type of the return value.

The former traditionally return a reference to their operand, and the latter, an independent temporary object . (What kind of "bit copy" you are talking about is not clear. The result of the operator usually contains a new value, so it is not clear how it can be a "copy" of something.)

In your example, the assignment operator is implemented just right, but the binary addition operator is implemented terribly crooked.

Your binary addition operator modifies its left operand. Where have you seen it? This is a strange and unexpected behavior for a binary addition operator.

The proper implementation might look like this.

 SomeClass operator +(const SomeClass &rhs) const { int new_value = value + rhs.value; return new_value; // <- Используется неявная конверсия `int` к `SomeClass` } 

Both operands retain their original values, and the return of the result, of course, is done by value.

But if you wanted to overload the compound assignment operator += , then it would look like this

 SomeClass &operator +=(const SomeClass &rhs) { value += rhs.value; return *this; } 

those. exactly as you wrote, but with the return on the link.

Separately, it should be added that, firstly, such "symmetric" operators with respect to their arguments, such as binary + , - , * , etc. It is recommended to overload with a separate function, not a member function (for example, a friend function). Secondly, if you overload both binary operators and the corresponding compound assignments, then the common idiom is to implement the first through the second

 class SomeClass { SomeClass& operator +=(const SomeClass &rhs) { value += rhs.value; return *this; } friend SomeClass operator +(SomeClass lhs, const SomeClass &rhs) { // Обратите внимание, что `lhs` передается по значению lhs += rhs; return lhs; } ... }; 

"Symmetric" operators are best defined by a separate function, rather than a member function, in order for such operators to behave "symmetrically" with respect to implicit coercions of argument types.

For example, if you set the binary operator + as shown in my first example, i.e. member function, the following unpleasant asymmetry will be observed

 SomeClass a(1), b(2); a = b + 1; // <- OK a = 1 + b; // <- Ошибка: нет такого оператора `+` 

those. such an operator will not be considered as a candidate in 1 + b expressions. Member functions are considered only from the left operand. And in the case of 1 + b left operand is 1 , which has no member functions at all.

But as soon as you make your binary + independent function, it will be applied in a symmetric way in both variants.

  • If it is not difficult, please clarify why it is better to set "symmetric" operators by a separate function, and not by a member. - Vladimir Gamalyan
  • one
    @Vladimir Gamalian: Added in response. - AnT
  • Why do you have a friend version of the operator + left operand is modified with += ??? they themselves attribute this to bad practice - that is, it must return the result without modifying the operands at all! and it would be more correct += overload via + and the assignment operator = (although it was probably better to just leave += unchanged), and not vice versa - ampawd
  • @ampawd: This is my mistake. Thanks for noticing. In my original version, applying += to lhs would not even compile because of the constancy of lhs . In the framework of such an implementation, the left operand of the binary + should be transmitted by value and without const . Corrected. - AnT
  • @AnT I'm not talking about constancy, but about the fact that the friend version of the operator + changes the left operand, why? you called it a bad practice, it should return the result without modifying any of the operands lhs rhs - ampawd

It’s not clear what this class is for, but there are several flaws in it. First, although it would be meaningless in your example, try to always take into account the rule of three - that is, in addition to operator overload = also define the copy constructor and destructor.

Secondly, use initialization lists, instead of initialization in the body of the constructor:

 SomeClass(int value): value(value) {} 

And you can return here and the link here, why once again copy the object?

 SomeClass& operator + (const SomeClass &rhs) { value += rhs.value; return *this; } 

ps Yes, and just do it, my advice to you, implement for example the class String , or the class for working with matrices - then such questions by a few will begin to disappear

  • 3
    @ampawd: You are actually helping the author implement the + operator, which actually behaves like the += operator. This is a very vicious practice. - AnT
  • @AnT is not at all, this class is not clear for what just do, therefore there is no fundamental difference in operator overload, well, it incorrectly overloads + , I did not notice this, but this is not the point, but that the author learned how to correctly write classes using more life examples, for example, implemented a string class, some data structures, a class for working with matrices, a smart pointer, etc. Then, following the logic of the behavior of these classes, it automatically begins to overload the operators - ampawd