13

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.

Keith M Smith
  • 133
  • 1
  • 6

7 Answers7

25

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.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • None of the answers answered my question directly. 'Has anyone found the need to declare the return parameter of a copy assignment operator const?' All the answers indirectly said 'No, I have not found a need... ' and the reason is because it violates the rule stated in this answer. My question arose from a conversation I had with someone who said they did return const. They were trying to prevent copying of the object. Not having the details, I believed the overall design was bad. Thought I would ask to see if there was a legitimate reason to return const. Thanks for all the answers! – Keith M Smith Apr 19 '10 at 15:55
  • @Keith: OK, my answer is also 'no'. – CB Bailey Apr 19 '10 at 16:00
  • Charles, can you add a quote from the standard? – Eduard Wirch Dec 25 '14 at 21:07
11

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.

R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
  • 1
    Actually - it should also be up to the library to prevent users from doing something the library isn't prepared to handle. – Michael Burr Apr 15 '10 at 21:51
  • 1
    @MichaelBurr - agreed, but that is not the issue here. The above is just short hand notation for `a = b; a.non_const_method();` – R Samuel Klatchko Apr 15 '10 at 21:59
  • 2
    @R Samuel Klatchko: I suppose you're right... That said - I use a non-const& return for `operator=()`, but if I had even the slightest concern that such a result might be used dangerously somehow I'd have a class return a `const&` instead and let users live with the inconvenience of not being able to chain some method calls off the operator. That might be somewhat lazy or paranoid, but I'll stipulate to that. – Michael Burr Apr 15 '10 at 22:23
  • let's be particular: `a=b=c;` wouldn't work. and that's a very common thing to do. – wilhelmtell Apr 15 '10 at 22:38
  • 3
    @willhelmtell - `operator=` returning const will still allow `a = b = c;` but will prevent `(a = b) = c;` – R Samuel Klatchko Apr 15 '10 at 22:54
6

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...

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
3

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
  }
  ...
};
Péter Török
  • 114,404
  • 31
  • 268
  • 329
2

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;
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
0

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

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
-1

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!
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 2
    I'm confused...that's normal. – GManNickG Apr 15 '10 at 21:33
  • 7
    "No it shouldn't be const, because it breaks the following code: ... (a = b) = c; // Yikes!". :) – Johannes Schaub - litb Apr 15 '10 at 21:33
  • 3
    But then you are violating the convention set by all the built-in types and types like `vector`, `string`, etc. Unless you have a real reason, just "do what the ints do". I think Scott Meyers coined that phrase. – Brian Neal Apr 15 '10 at 21:34
  • 2
    Also, the implicitly declared operator= returns `T&`, so if you are consequent, you have to always provide your own operator=. I think that's not worthwhile. – Johannes Schaub - litb Apr 15 '10 at 21:35
  • @Johannes Schaub - litb, @GMan:, @Brian Neal: The parenthesis around `(a = b)` are what make it wrong. Remove the ()s and it's perfectly okay. – Billy ONeal Apr 15 '10 at 21:39
  • Why return a `MyClass`-anything? Why not just return `void` if you don't want the result to be used? – Potatoswatter Apr 15 '10 at 22:11
  • @Billy: What should be wrong with the parenthesis case? Going with your `int` argumentation, `(i1=i2)=i3` is perfectly fine. – Georg Fritzsche Apr 15 '10 at 22:12
  • 2
    Writing `(a = b) = c` means one of two things - either `operator=` has an unusual post-condition, meaning that the code has a different result from `a = c`, or else the first assignment is redundant. Assuming a "normal" operator=, I don't see any reason to support it. I also don't see any reason to go out of my way to prevent it. There are cases where const-safety makes it harder to write bad code, but I don't believe this is one of them. So I think this is a non-reason. – Steve Jessop Apr 15 '10 at 23:02
  • @gf: Just because something compiles does not mean it's what clients want. In 99% of all cases, that is a typo. – Billy ONeal Apr 16 '10 at 00:24
  • Actually i somehow misread *Brian Neal* for *Billy ONeal* above, thus the `int` argumentation reference... *sigh* ... As for the typos... there are too many features in C++ that can be used erroneously - disallowing other valid uses just to take care of possible typos doesn't help either. – Georg Fritzsche Apr 16 '10 at 01:51
  • @Potatoswatter - if you have `operator=` return void, you can't do such common idioms as `a = b = c` or `if ((a = foo()) == value)`. Having `operator=` return itself allows for much common shorthand. – R Samuel Klatchko Apr 16 '10 at 04:35