5

Look at the following code:

#include <iostream>
using namespace std;

class Widet{
public:
    Widet(int val = 0):value(val)
    {

    }

    Widet& operator=(Widet &rhs)
    {
        value = rhs.value;
        return *this;
    }
    int getValue()
    {
        return value;
    }
private:
    int value;
};

int main()
{
    Widet obj1(1);
    Widet obj2(2);
    Widet obj3(0);
    (obj3 = obj2) = obj1;
    cout << "obj3 = " << obj3.getValue() << endl;
}

The code runs successfully and the output is (using VS2008):

enter image description here

When I let the operator= return a value instead of reference:

Widet operator=(Widet &rhs)
{
    value = rhs.value;
    return *this;
}

It also runs successfully and the output is :

enter image description here

My question is :Why the second code runs well?Should not we get a error?

Why it is a good habit to return reference to *this instead of *this?

XiaJun
  • 1,855
  • 4
  • 24
  • 41

7 Answers7

8

Why the second code runs well?Should not we get a error?

Because it's perfectly valid code. It returns a temporary copy of the object, and you're allowed to call member functions (including operator=()) on temporary objects, so there is no error.

You would get an error if the object were uncopyable.

Why it is a good habit to return reference to *this instead of *this?

Because not all objects are copyable, and some objects are expensive to copy. You can take a reference to any object, and references are always cheap to pass around.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    It should be added that if you really want to return a temporary, say from `operator+()`, it should always be `const` to avoid the user from modifying it by mistake (See Meyers, Effective C++, Item 3). – Fabian Knorr Aug 20 '12 at 14:25
  • @trion: No, it shouldn't be `const`, because that prevents you from taking an _rvalue_ reference to it. – Mike Seymour Aug 20 '12 at 14:31
  • That's not true. Functions always return rvalues, therefore you can take rvalue references to it. Try compiling `const int foo() { return 0; } int &&rr = foo();` and see for yourself. – Fabian Knorr Aug 20 '12 at 15:27
  • @trion: Try compiling `struct t{}; const t foo() {return {};} t &&rr = foo();` and see for yourself. (To be honest, I'm not quite sure why your example appears to work, but it definitely doesn't work for user-defined types in general). – Mike Seymour Aug 20 '12 at 16:14
  • @trion: As noted by Meyers [here](http://aristeia.com/BookErrata/ec++3e-errata.html); search for "rvalue reference". – Mike Seymour Aug 20 '12 at 16:17
  • Well, that's interesting. So it looks like there is no reasonable way to prohibit a statement like `t++++`? – Fabian Knorr Aug 20 '12 at 17:04
  • @MikeSeymour trion's example above works for `const int` because `const` is being ignored for non-class return types, so a rvalue reference will actually bound to an `int` and not `const int`. `[expr]/6: If a prvalue initially has the type “cv T,” where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.` – vsoftco Feb 27 '15 at 06:57
3

Usually, You return a reference so that one can use the = operator as an l-value.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
3

When you don't return a reference, (obj3 = obj2) gives a temporary copy of obj3. The copy obtains the value from obj1 and is deleted, while obje3 is never affected by the second assignment.

Grzegorz Herman
  • 1,875
  • 11
  • 22
2

Why the second code runs well?Should not we get a error?

It runs because nonconst member functions can be called on (nonconst) class rvalues as well. The second version of operator= returns a nonconst class rvalue, so in effect, you assign to the temporary, leaving the previous value in the obj3 variable.

Therefore, there is no error.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
0

In the second example you create a temporary (a copy of Obj3, returned by operator=) and assign Obj1 to it. Then it immediately gets destructed. Obj3 remains the result of first assignment - Obj3 = Obj2.

Steed
  • 1,292
  • 1
  • 14
  • 33
0

Returning a reference from operator=() enables expressions like:

a=b=c;

Returning a value may be excessive when you don't need it. It can cause extra copy-constructor/destructor calls. Otherwise, returning a value is perfectly valid C++. People, please correct me if I'm wrong, but I think returning by value is not that big of an issue in C++11 because of move semantics.

Alexander Chertov
  • 2,070
  • 13
  • 16
  • But ,If I return a value instead of reference, are the following expressions unabled : a=b=c; ??? – XiaJun Aug 20 '12 at 13:43
  • @XiaJun, yea, if you return a value then `a=b=c;` is still valid. I was wrong, sorry about that. You just need to be sure you have a const specifier (because temporary objects can be implicitly converted to const references only): `Widet operator=(const Widet &rhs) `. – Alexander Chertov Aug 20 '12 at 17:23
0
(obj3 = obj2)

can be considered as obj3operator=(obj2) //hypothetically.
Since you have passed obj2 as parameter, your operator overload will copy the obj2.value into obj3.value.

(obj3 = obj2) = obj1;
After the return of from operator=, obj3(*this,temporary copy) will be returned.
So the equivalent code becomes obj3=obj1 which will again invoke operator= of obj3 and will reset value of obj3.value to obj1.value i.e. 1.

perilbrain
  • 7,961
  • 2
  • 27
  • 35