1

This is from a C++ screening test from '03 or '04.

Which of the following declares the assignment operator of MyClass so that the code on Line 3 generates a compilation error while the code on line 2 does not?

Consider the following code fragment:

   (Line 1) MyClass a, b, c; 
   (Line 2) a=b=c; 
   (Line 3) (a=b)=c; 

1) void operator=( const MyClass& X );
2) MyClass operator=( const MyClass& X );
3) const MyClass operator=( const MyClass& X );
4) None of the above

The correct answer is 3). Now, how is this so? How does it work? What is calling what? Do the parens force a temporary to be created? How does const get involved in the mix?

Community
  • 1
  • 1
Shellsunde
  • 76
  • 10
  • A stupid question about const correctness and useless const return values (which are no references) –  Jun 25 '14 at 19:32
  • I very much agree, but I didn't have the basis for making that attack. I got it wrong eleven years ago, and felt abused by having to know what I then felt was a 'gotcha', a petty detail. I came across it today and now, I would like to know. – Shellsunde Jun 25 '14 at 19:52
  • It's a contrived situation, but it does test how well one understands the mechanics of const, operator overloading, and associativity. That said, it probably would have been better for them to use an example of code that would actually make sense to write in the real world. But maybe that was the point and they were looking for you to say "the correct answer is 3, but it's a dumb question." :) – dlf Jun 25 '14 at 19:57
  • For bonus points, why is it a bad idea in C++11? – Mike Seymour Jun 26 '14 at 00:55
  • I would recommend to return references instead of copies, i.e. 2) MyClass & operator=( const MyClass& X ); and 3) const MyClass & operator=( const MyClass& X ); – Fabian Apr 19 '17 at 06:29

4 Answers4

4

Assignment is right associative, so

a=b=c

is equivalent to

a=(b=c)

By making operator= return a const value, you can no longer assign to it, so

(a=b)=c

doesn't work, but that doesn't affect a=(b=c), since you are reading the result of the assignment instead of writing to it.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
4

It may help to rewrite the assignments as function calls instead of operators (as others have stated explicitly, without any (), assignment proceeds right to left):

Line 2: a.operator=(b.operator=(c));
Line 3: (a.operator=(b)).operator=(c);

This makes it a little more clear that on line 3, you are trying to take the result of a=b and assign c to that. But in case 3, the output of operator= is a const object, so this is not allowed (since operator= is a nonconst function).

It would probably be even more clear to break each assignment into two separate statements:

// with const MyClass MyClass::operator=( const MyClass& X );

// line 1
MyClass a, b, c; 

//line 2; originally a=b=c
const MyClass d = (b = c);
a = d; // no problem; a is nonconst, and it's ok for d (the parameter to operator=) to be const

//line 3; originally (a=b)=c
const MyClass e = (a = b); // note: the leftmost = here is initialization rather than assignment
e = c; // no good! e is const, and you can't call a nonconst function like operator= on a const object

Or take it one step further and get rid of any operator-related confusion by just using a function named assign in place of operator=:

// with const MyClass MyClass::assign( const MyClass& X );

// line 1
MyClass a, b, c; 

//line 2; originally a=b=c
const MyClass d = b.assign(c);
a.assign(d); // ok. assign() is nonconst, but so is a.

//line 3; originally (a=b)=c
const MyClass e = a.assign(b);
e.assign(c); // no good! e is const and assign() is not.
dlf
  • 9,045
  • 4
  • 32
  • 58
  • Yes, this helps, but I still don't fully understand. In both cases, a=(b=c) and (a=b)=c, isn't there an assignment to a const object, in the first case, to _b_ by _c_, and in the second to _a_ by _c_? So, shouldn't both fail to compile? – Shellsunde Jun 25 '14 at 20:20
  • With option 3 in effect, in the `a=(b=c)` case the return value from `b=c` will indeed be const, but this result (call it d) then gets used as the *input parameter* to `a.operator=(d)`. And the parameter/right-hand-side is allowed to be const; it's just the left-hand-side that isn't. – dlf Jun 25 '14 at 20:28
  • In the first case, the return value from `b=c` (which is not `c`; the way the code is written, it's a temporary rvalue) is assigned to `a`. `a` is not const, so that's fine. In the second case, you try to assign `c` to the return value of `a=b` (again; this is a temporary), but that return value is const, so you can't. – dlf Jun 25 '14 at 20:31
  • Glad you understand; guess our comments interleaved a bit. – dlf Jun 25 '14 at 20:32
  • In the 2nd case, before you try to assign `c` to the return value of `a=b`, you have to have assigned `b` to `a`. Is that allowed because it won't be retained, since it is only to be used this once as the argument for an assignment? – Shellsunde Jun 25 '14 at 20:39
  • Your first sentence is correct, but I'm not I follow the second. Does the edit I just made to my answer help? – dlf Jun 25 '14 at 20:44
  • Your edit makes your comment even more clear. To explain my last question, I asked: Although the 1st assignment is to a const, it's allowed. Why? Because it results in a temporary or because it's a temporary and will be used for an assignment? – Shellsunde Jun 25 '14 at 20:58
  • If I understand, you may be conflating initialization with assignment. The `=` in e.g. `const int x = 3;` is allowed because this is initialization. But `const int x = 3; x = 4;` fails because the second `=` is an assignment, which is not allowed. – dlf Jun 25 '14 at 21:00
  • OK, got it. I was conflating the two. – Shellsunde Jun 25 '14 at 21:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56303/discussion-between-shellsunde-and-dlf). – Shellsunde Jun 25 '14 at 21:03
2

Assignment is right-associative, so line 2 is equivalent to:

a = (b = c);

That means there must be an assignment from MyClass to MyClass, with a return-type which can be assigned to a MyClass.

The 2nd and 3rd option obviously fill those requirements (as would letting the compiler generate default-assignment operators).

The third line though requires that a MyClass can be assigned to the return value of the assignment of a MyClass to a MyClass, which only the 2nd option allows, though with bad semantics.

Combining that, the question asks for option 3.

The assignment operator should return a reference to the left-hand object, without any cv-qualifiers, for proper chaining.
Read this for details and background of operator overloading: Operator overloading

Community
  • 1
  • 1
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
1

Both lines make 2 assignments. When broken into multi-line statments they look like this:

Line 2: a=b
        b=c
Line 3: a=b
        a=c

The equals operator returns the left hand side. Since choice 3 returns a const MyClass an error will occur in Line 3 when an attempt is made to re-assign a constant variable.

Rainbacon
  • 933
  • 1
  • 16
  • 24