1

Operator overloading

shows this is done in the following way

class X {
  X& operator+=(const X& rhs)
  {
    // actual addition of rhs to *this
    return *this;
  }
};
inline X operator+(X lhs, const X& rhs)
{
  lhs += rhs;
  return lhs;
}

I understand why the argument lhs needs to be taken by value, but why do we need to return it by value? I mean, what's the problem with the following:

inline X& operator+(X lhs, const X& rhs)
{
  lhs += rhs;
  return lhs;
}

Inside the function, lhs is a new X and modifying it does not affect any of the two operands. Since this is new, we can return it via reference, right?

Community
  • 1
  • 1
prongs
  • 9,422
  • 21
  • 67
  • 105

3 Answers3

3

The problem of returning a reference to a local object is that the caller will receive something that is already dead on arrival.

When you exit from a function all locals are destroyed, so you cannot keep using references to them.

Code like

const X& res = a+b;

would works if the operator returns by value because there is a specific rule in the language that says that temporary objects bound to references will be kept alive until the reference itself is destroyed.

Returning a reference instead is different because the lifetime of the object is a responsibility of the called operator, not of the caller. So the end result will be a reference that is bound to an object that has already been destroyed.

Even just

X res;
res = a+b;

would not work, because when the operator call returns the object is destroyed before the assignment is made to res. Same would be in case of a copy constructor X res = a+b;.

6502
  • 112,025
  • 15
  • 165
  • 265
0

Never return ba reference if the value you are referencing will cease to exist after exiting the function.

In this case, lhs will exist until the function returns. If you return by reference, that reference will "point" to a value that is no more, and any attempt to use it will result in undefined behavior.

That's the reason for return by value. But no worries, many compilers can optimize the additional copy away, and it is in fact no copy but a move operation, so even if it's not optimized away, "heavy" object will be moved and not copied since C++11.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
0

Not strictly answering the question, but to aid understanding about references ... be very cautious about returning a reference, except if you know the referent (object being referred to) is stable.

It is unsafe to do this

const std::string& doNothing(const std::string& x) {return x;}

You will become unstuck on the second call shown below

std::string baa = "sheep";
std::cout << doNothing(baa) << std::endl;    // works
std::cout << doNothing("goat") << std::endl; // UNSAFE

This is because the second call involves the implicit creation of a temporary object (of type std::string using constructor call string("goat"))

By contrast, it is safe to return a reference from name()

class Person
{
  private: std::string _name;
  public: const std::string& name() { return _name; }
  // other constructors and ways to set a _name
};

One can be reasonably assured that the class object (and therefore its internal _name) will stick around long enough for the reference to be useful to the caller.

Kiwi Nick
  • 150
  • 1
  • 10