2

I am a student of computer science. Please read my question fully before answering

In C++ class today we learned about overloaded operators, in particular the assignment operator, and my professor said something he doesn't like to: "Just trust me." He uttered this line in reference to the return *this convention.

I asked, "But why?" and his answer was pretty much, "Because it is."

I am not satisfied with this answer.

Consider the following:

class length
{
private:
     int inches, feet, yards;
public:
    //getters, setters, etc.
    Length operator=(const Length& Q)
    {
        this->inches = Q.inches;
        this->feet = Q.feet;
        this->yards = Q.yards;
        return *this;
    }
};

I understand based on mucking about through countless questions and at least 3 C++ books that the "Convention" exists because it allows chained assignments but why is it necessary and how did it come to be?

A more in-depth question is how does this have individual "child pointers" (my term, not official) to the properties of the class (IE this->inches)? How does that work? Is this->inches just an offset or something?

My professor and I would REALLY appreciate an answer that isn't just "Because it's how it's done."

Please and Thank You

edit: I thought I was clear in writing the question; however, based on the responses I have been getting, I believe there has been a miscommunication. I am seeking to understand where the convention came from (roots in C I believe) and why it is this way.

karnesJ.R
  • 326
  • 1
  • 11
  • 2
    @MartinJ. -- Good find, but to anyone else voting to close, please look past the accepted answer. Then consider downvoting it, and upvoting a correct one. – Benjamin Lindley Feb 21 '14 at 02:33
  • 4
    If your professor can't come up with a better answer than "because it's how it's done" you should get a new professor. – Captain Obvlious Feb 21 '14 at 02:33
  • My personal opinion is that returning a pointer is the technically correct thing to do, but that would be too 'C' like. Gotta be like those other trendy languages and try to hide the existence of pointers. – Joshua Clayton Feb 21 '14 at 03:13
  • @JoshuaClayton: In what way is that 'C' like? When you do an assignment from one variable to another in C, the type of the expression is not a pointer (unless of course, the thing you're assigning to *is* a pointer). – Benjamin Lindley Feb 21 '14 at 03:16
  • An assignment needs only two operands. The object and the single argument fulfil this. Returning a pointer would be just as valid as this reference. Returning void would make more sense to me. – Joshua Clayton Feb 21 '14 at 03:22
  • OK. Martins answer of daisy chaining assignments makes sense. Goes back to my first comment about conflating (trying to harmonize) objects with POD data types. In C, we treat arrays and structs as distinctly different from the numeric data they contain. – Joshua Clayton Feb 21 '14 at 03:26
  • So how then do you explain the `this->inches` section? That little section of the question hasn't been addressed. It seems like everyone is fixated on the `return *this` – karnesJ.R Feb 21 '14 at 17:16

3 Answers3

3

The consensus seems to be that operator= traditionally returns *this to enable constructs such as:

a = b = c

which is the traditional behavior of C extended to classes.
By the way, the signature of your operator= should probably be:

Length& operator=(const Length& Q);

because you don't want to create a new temporary object sa return value

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
Martin J.
  • 5,028
  • 4
  • 24
  • 41
  • Just out of curiosity, why do I not want to return a TNO (temporary nameless object)? In `operator+(const Length& Q)` we definitely return a TNO on purpose, so why not here in assignment? – karnesJ.R Feb 21 '14 at 17:17
3

Since you are not satisfied with the answer "Because it is", let me give you a different one:

When overloading operators, do as int does

The reason for that recommendation is to follow the principle of least surprise, people are used to the operator looking and behaving in a particular way for the existing types. When they use the operator with your type, there is an implied expectation that it will behave the same.

Now the next question is why do int behave this way? For that I don't have a clear answer. It enables some fancy syntax like: (a = 3) = 5, ++(a = 3) or (x = y).change(), but it is unclear what the real value is in any of those three cases. In the first case, you can directly assign 5, in the other two cases rewriting the expression as two separate expressions would make the code more readable.

I intentionally avoided the case of a = b = c in the previous argument, as this case supports the idea that the operator should yield a value, or a reference so that it can be used in a later assignment, but the convention is to return a modifiable reference, and the expression above does not required this. A const & would suffice to support the above syntax. [And then again, is that syntax really needed? What is the cost of splitting that into two statements? Would it not be more readable? (in many cases it might be better, in some case it might not)].

At any rate the lesson to take home is to follow the principle of least surprise, and whatever the rationale that led to this behavior for all fundamental or otherwise standard types, once it becomes an idiom it is better to just follow it.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
2

Assignment is an expression. It has both an effect (copying some data from its right-hand side to its left-hand side) and a value. Unless you want to break convention and make weird things happen, that value is the thing on the left hand side (i.e. this). That way if (a = b) is true if a became true from the assignment, and a = b = c assigns c to both a and b.

The reason it's best to return this by reference, and not by value, as stated in this answer, is that it avoids unnecessary copying.

Community
  • 1
  • 1
hobbs
  • 223,387
  • 19
  • 210
  • 288
  • Yep. Assignment returning a value promotes brevity. – jthill Feb 21 '14 at 02:36
  • For interest: there are some languages (like Python) where assignment *isn't* an expression: it's a type of statement, and it has a side-effect, but it doesn't have a value because it can't be used *within* a statement. – hobbs Feb 21 '14 at 02:39
  • 2
    Formally speaking, `a = b = c` does not assign `c` to both `a` and `b`. It only assigns `c` to `b`, and then assigns the result of `b = c` to `a`. The result of `b = c` might be quite different from `c` itself. Even with built-in operators, `a` is assigned `c` converted to the type of `b`, which is also not `c` itself. For example, in `int a; unsigned char b; a = b = -1;` variable `a` will receive *positive* value. – AnT stands with Russia Feb 21 '14 at 02:39
  • A mention of `void operator=(bob const&)` might also be worthwhile. – Yakk - Adam Nevraumont Feb 21 '14 at 03:07
  • Note that the return type is a non-const reference to support `(a = b) = c;` which is valid for `int` in C (*when overloading an operator, do as `int` does*), whether that expression is really meaningful or not… otherwise you can consider `(a = b).change()` as an option… but I am yet to see a good use of a non-const reference returned from `operator=` in production code – David Rodríguez - dribeas Feb 21 '14 at 04:06