1

I was surprised to see a bit of syntax in a co-worker's code today.

void doSomething(bool& boolRef);

bool ok = true;
doSomething(ok = false);

I would think this is an error since the assignment operator in the param, ok = false, returns the value of the assigned value, which in this case is false. And since doSomething is expecting a reference, my first reaction was "that shouldn't work" since it would seem almost identical to doing doSomething(false);

But alas it does work. And reassigning the value of reference inside of doSomething works just like you'd expect.

Can someone please explain why this works?

Thank you!

Addy
  • 2,414
  • 1
  • 23
  • 43

2 Answers2

6

The canonical assignment operator for a class Foo looks like this:

class Foo {
  ...

  Foo& operator=(const Foo& other) {
    // make this the same as other
    return *this;
  }
};

As you can see, it does return a reference. In this case the object is ok, and so it returns a reference to ok, which is an lvalue, which is why the call works.

Bool of course is not a class but nonetheless it works the same way, this is why the canonical assignment operator is written just like this.

As an aside, I personally don't really care for exploiting this very much and I would probably have written the doSomething(ok=false) as two lines, and I suspect many other people would too.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
  • I think you can remove "probably". Canonical assignment should be written the same way they work for embedded types. – Slava Aug 23 '17 at 19:15
  • @Slava I wrote probably only because strictly speaking I'm speculating as to what was on the mind of whoever decided that this would be the canonical form, but I see your point, I'll remove it. – Nir Friedman Aug 23 '17 at 19:16
  • I'd say as of C++11 this is not canonical anymore. Sometimes the pass-by-value version is better. – Brian Bi Aug 23 '17 at 19:19
  • @Brian Not really. Pass-by-value usually means writing swap by hand. There are multiple performance issues with this: implementing move assignment in terms of swap is inefficient, copy construction is often inefficient compared to assignment, etc. The core guidelines specifically suggest passing by `const&`. It can save a very small amount of code duplication, in very specific circumstances; usually code duplication is better avoided with the rule of zero. Hinnant and Meyers have both recommended against it. Etc. If you have a good source arguing its merits I'd be curious to see it. – Nir Friedman Aug 23 '17 at 19:29
3

Assignment returns a reference to the object being assigned to (not the object being assigned from), which in this case is the lvalue ok, so the code is also ok, though perhaps not great.