31

I am reading a book about C++ and more precisely about the operator overloading.

The example is the following:

const Array &Array::operator=(const Array &right)
{
// check self-assignment
// if not self- assignment do the copying
return *this; //enables x=y=z
}

The explanation provided by the book about returning const ref instead of ref is to avoid assignments such as (x=y)=z. I don't understand why we should avoid this. I understand that x=y is evaluated first in this example and since it returns a const reference the =z part cannot be executed. But why?

alampada
  • 2,329
  • 1
  • 23
  • 18
  • 4
    Which book? That looks like an unnecessary precaution to me. I can't imagine somebody writing `(x=y)=z` -- why would they? And without the parens, `x=y=z` is parsed as `x=(y=z)`, which makes perfect sense, so there's no risk there. – j_random_hacker Jan 16 '11 at 16:59
  • 1
    But why what? Why is it a const ref? Why is it executed in this order? Why can't `z` be assigned to (x=y)? – Lazarus Jan 16 '11 at 16:59
  • 18
    @antronis: get a better C++ book. – Yakov Galka Jan 16 '11 at 17:00
  • it's deitel c++ how to program 7th edition – alampada Jan 16 '11 at 17:01
  • 3
    I agree that it's not something you would usually write, but what if you meant `(x = y) == z` and mistyped? I'm not endorsing that particular expression either, but making more things const helps turn runtime errors into compile time errors, which is generally helpful. – Asher Dunn Jan 16 '11 at 17:07
  • I think this is a bad idea. When defining our own types we try and make the operators behave in the same way as the built-in types behave (so that it is not a surprise to users of the code how they work). If you try the code replacing `Array` with `int` it works. Your code should work in the same way. – Martin York Jan 16 '11 at 17:35

5 Answers5

33

(x=y) means x.operator=(y), which returns the object x. Therefore, (x=y)=z means (x.operator=(y)).operator=(z). The expression in parens sets x to y and returns x, and then the outer bit sets x to z. It does not set y to z as you might expect, and as the expression x = y = z does.

This behavior is counter-intuitive (they should all be equal after the assignment, right?); returning a const reference makes it impossible and avoids the problem.

Asher Dunn
  • 2,384
  • 16
  • 12
  • 2
    While the behavior sounds counter-intuitive, it isn't a "problem" because it is something the programmer would have intentionally done (Which is why they used brackets) – James Jan 16 '11 at 17:08
  • 25
    Who would expect `(x=y)=z` to set `y` to `z`? This is very contrived "problem" and a C++ book shouldn't spend time explaining precautions against it. `(x=5)=z` doesn't set `5` to `z`. – Fred Foo Jan 16 '11 at 17:09
  • 1
    If you expect assignment to be associative (like comparison is), you would expect all three to be equal at the end. Naive, but a reasonable first-glance interpretation. I agree that this form shouldn't generally be used and shouldn't be emphasized in the book, but it's good to help the compiler help you *not* shoot yourself in the foot whenever possible =) – Asher Dunn Jan 16 '11 at 17:19
  • 3
    The first thing to be understood about assignment is that it is not associative. Every imperative programming textbook that I've seen explains that in chapter 1 or 2. Your comment about mistyping `(x=y) == z` makes more sense. – Fred Foo Jan 16 '11 at 17:29
  • It may not be intuitive. **BUT** it is exactly the way it works with integers. I think mimicking the behavior of the standard types is more important than trying to protect people from obscure situations. – Martin York Jan 16 '11 at 17:33
  • I want to be able to do: `(SelectedMesh = Mesh1).SetTexture(tex);` or `(Normal = l1.Cross(l2)).Normalize();` . What benefits gives me to return a const in such assignments? – Pablo Ariel Jan 22 '16 at 03:38
  • In support of @MartinYork is Donnie Pinkston at CalTech who says in his C++ Operator Overloading Guidelines, http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html, '''The rule of thumb is, "If it's good enough for ints, it's good enough for user-defined data-types." – Daniel Ford Sep 21 '21 at 16:29
22

There is no need to avoid this, unless the book is aimed at programmers that commonly write (x=y)=z when they mean x=y=z. In practice, nobody in their right mind writes that, so the precaution is entirely unnecessary. It also forbids some other terse constructs, such as (x=y).nonConstMember(), that hardly anyone writes but that might be useful in some contexts (although they shouldn't be over-used).

@ybungalobill is right, get a better book.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 10
    +1. A programmer "accidentally" writing `(x=y)=z` seems about as likely (and as necessary to guard against) as a programmer accidentally writing `x=y+system("rm -rf /")` when they meant `x=y`. – j_random_hacker Jan 16 '11 at 17:40
  • 1
    But I have seen `if ((x = y) = z) ...` when the author meant `if ((x = y) == z) ...`. – Adrian McCarthy Sep 28 '16 at 16:56
14

As far as I know, assignment operators do not return const references in idiomatic C++. The Standard types do not return const references either.

std::string a, b, c;
(a = b).clear(); // no objection from compiler

All of my custom assignment operators have returned a non-const reference.

When in doubt, check the Standard library. It's not flawless, but it definitely gets basic things like this correct.

Puppy
  • 144,682
  • 38
  • 256
  • 465
4

I would look at the behavior of the built-in types.

When defining your own types it is preferable that the operators behave the same way as the built-in types. This allows easy adoption of your classes without having to dig into your code to see why they behave differently from expected.

So if we look at integers:

int main()
{
    int x = 5;
    int y = 6;
    int z = 7;

    (x = y) = z;
    std::cout << x << " " << y << " " << z << "\n";
}

This works with y unchanged and x being assigned 7. In your code i would expect your assignment operator to work the same way. The standard assignment operator definition:

Array& Array::operator=(Array const& rhs)
{
    /* STUFF */
    return *this;
}

Should do that just fine (assuming /* STUFF */ is correct).

Martin York
  • 257,169
  • 86
  • 333
  • 562
1

The only reason I can see is that this book was written to explain C++ to C programmers (or by an author whose C understanding is better than C++ understanding). Because for a C programmer, the expression (x = y) = z is invalid for built-in types, and he probably will to try to get the same behavior with its user-defined types.

However, C and C++ are different languages, and in C++ the expression (x = y) = z is valid even for built-in types. So if you want to have the same behavior for your user-defined types, you should return a non-const reference in the operator =.

I would advise you to get a better book, one that does not make the confusion between C and C++. They are not the same langages, even if they derive from a common base.

Sylvain Defresne
  • 42,429
  • 12
  • 75
  • 85