11
votes

The copy assignment operator has the usual signature:

    my_class & operator = (my_class const & rhs);

Does the following signature have any practical use?

    my_class const & operator = (my_class const & rhs);

You can only define one or the other, but not both.

7
you can define both, if one of then is a const method. :pwilhelmtell

7 Answers

23
votes

The principle reason to make the return type of copy-assignment a non-const reference is that it is a requirement for "Assignable" in the standard.

If you make the return type a const reference then your class won't meet the requirements for use in any of the standard library containers.

9
votes

Don't do that. It prevent a client from writing something like:

(a = b).non_const_method();

instead of the longer form:

a = b;
a.non_const_method();

While you may not like the shorthand style, it's really up to the user of the library to decide how they want to write the code.

5
votes

An answer that mirrors one from Overloading assignment operator in C++:

Returning a const& will still allow 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 the result 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 serves as a sequence point.

I see no problem returning a const& unless you want to specifically allow the lvalue semantics (and design the class to ensure it acts sensibly with those semantics). If you're users want to do something unusual with the result of operator=(), I'd prefer that the class disallow it rather than hope it gets it right by chance instead of design.

Also. note that while you said:

You can only define one or the other, but not both.

that's because the function signature in C++ doesn't take into account the return value type. You could however have multiple operator=() assignement operatiors that take different parameters and return different types appropriate to the parameter types:

my_class& operator=( my_class& rhs);
my_class const& operator=(my_class const& rhs);

I'm not entirely sure what this would buy you though. The object being assigned to (that is presumably the reference being returned) is non-const in both cases, so there's no logical reason to return a const& just because the righ-hand-side of the = is const. But maybe I'm missing something...

3
votes

Effective C++ explains that this would break compatibility with the built-in types of C++.

You can do this with plain ints:

(x = y) = z;

so he reasons, however silly this looks like, one should be able to do the same with one's own type as well.

This example is there in 2nd Edition, although not anymore in the 3rd. However, this quote from 3rd Ed., Item 10 tells the same still:

[...] assignment returns a reference to its left-hand argument, and that's the convention you should follow when you implement assignment operators for your classes:

class Widget {
public:
  ...
  Widget& operator=(const Widget& rhs)   // return type is a reference to
  {                                      // the current class
  ...
  return *this;                        // return the left-hand object
  }
  ...
};
2
votes

Why is everyone obsessing over (a = b) = c? Has that ever been written by accident?

There is probably some unforeseen utility of the result of assignment being altered. You don't just make arbitrary rules against made-up examples that look funny. Semantically there is no reason that it should be const, so do not declare it const for lexical side effects.

Here is an example of somewhat reasonable code that breaks for const & assignment:

my_class &ref = a = b;
0
votes

As in any other usage of const, const is the default, unless you really want to let the user change.

-1
votes

Yes, it should be const. Otherwise clients can do this:

class MyClass
{
public:
      MyClass & operator = (MyClass const & rhs);
}

void Foo() {
    MyClass a, b, c;
    (a = b) = c; //Yikes!
}