Overloading assignment operator in C++

2020-01-27 04:06发布

As I've understand, when overloading operator=, the return value should should be a non-const reference.


A& A::operator=( const A& )
{
    // check for self-assignment, do assignment

    return *this;
}

It is non-const to allow non-const member functions to be called in cases like:


( a = b ).f();

But why should it return a reference? In what instance will it give a problem if the return value is not declared a reference, let's say return by value?

It's assumed that copy constructor is implemented correctly.

10条回答
我想做一个坏孩纸
2楼-- · 2020-01-27 04:20

I'm not sure how often you'd want to do it, but something like: (a=b)=c; requires a reference to work.

Edit: Okay, there is a bit more to it than that. Much of the reasoning is semi-historical. There's more reason you don't want to return an rvalue than just avoiding an unnecessary copy into a temporary object. Using a (minor) variation on an example originally posted by Andrew Koenig, consider something like this:

struct Foo { 
    Foo const &assign(Foo const &other) { 
        return (*this = other);
    }
};

Now, assume you're using an old version of C++ where assignment returned an rvalue. In that case, (*this=other); will yield that temporary. You're then binding a reference to the temporary, destroying the temporary, and finally returning a dangling reference to the destroyed temporary.

Rules that have been enacted since (extending the life of a temporary used to initialize a reference) would at least mitigate (and might completely cure) this problem, but I doubt anybody re-visited this particular situation after those rules had been written. It was a bit like an ugly device driver that includes kludges to work around dozens of bugs in different versions and variations of the hardware -- it could probably be refactored and simplified, but nobody's quite sure when some seemingly innocuous change will break something that currently works, and ultimately nobody wants to even look at it if they can help it.

查看更多
祖国的老花朵
3楼-- · 2020-01-27 04:21

In real code (i.e. not things like (a=b)=c), returning a value is unlikely to cause any compile errors, but it is inefficient to return a copy because creating a copy can often be expensive.

You can obviously come up with situation where a reference is needed, but those rarely -- if ever -- come up in practice.

查看更多
一夜七次
4楼-- · 2020-01-27 04:21

If it returned a copy, it would require you to implement the copy constructor for almost all non-trivial objects.

Also it would cause problems if you declare the copy constructor private but leave the assignment operator public... you would get a compile error if you tried to use the assignment operator outside of the class or its instances.

Not to mention the more serious problems already mentioned. You don't want it to be a copy of the object, you really do want it to refer to the same object. Changes to one should be visible to both, and that doesn't work if you return a copy.

查看更多
Emotional °昔
5楼-- · 2020-01-27 04:25

Not returning a reference is a waste of resources and a yields a weird design. Why do you want to do a copy for all users of your operator even if almost all of them will discard that value?

a = b; // huh, why does this create an unnecessary copy?

In addition, it would be surprising to users of your class, since the built-in assignment operator doesn't copy likewise

int &a = (some_int = 0); // works
查看更多
祖国的老花朵
6楼-- · 2020-01-27 04:27

Cause f() can modify a. (we return a non-const reference)

If we return a value (a copy) of a, f() will modify the copy, not a

查看更多
孤傲高冷的网名
7楼-- · 2020-01-27 04:34

If you're worried that returning the wrong thing might silently cause unintended side effects, you could write your operator=() to return void. I've seen a fair bit of code that does this (I assume out of laziness or just not knowing what the return type should be rather than for 'safety'), and it causes few problems. The kind of expressions that need to use the reference normally returned by operator=() are pretty rarely used, and almost always easy code an alternative for.

I'm not sure I'd endorse returning void (in a code review I'd probably call it out as something you shouldn't do), but I'm throwing it out there as an option to consider if you want to not have to worry about how oddball uses of the assignment operator might be handled.


late edit:

Also, I should have originally mentioned that you can split the difference by having your operator=() return a const& - that will still permit assignment chaining:

a = b = c;

But will disallow some of the more unusual uses:

(a = b) = c;

Note that this makes the assignment operator have semantics similar to what it has in C, where the value returned by the = operator is not an lvalue. In C++, the standard changed it so the = operator returns the type of the left operand, so it is an lvalue, but as Steve Jessop noted in a comment to another answer, while that makes it so the compiler will accept

(a = b) = c;

even for built-ins, the result is undefined behavior for built-ins since a is modified twice with no intervening sequence point. That problem is avoided for non-builtins with an operator=() because the operator=() function call is a sequence point.

查看更多
登录 后发表回答