Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does cannonical implementation of overloading binary arithmatic operator in C++ pass first arguement by value?

According to this cppreference page, passing the first argument of an overloaded binary operator by value somehow optimizes expressions like a + b + c. The relevant code snippet from the linked page is as follows:

class X
{
 public:
  X& operator+=(const X& rhs)         // compound assignment (does not need to be a member,
  {                                   // but often is, to modify the private members)
    /* addition of rhs to *this takes place here */
    return *this;                     // return the result by reference
  }

  // friends defined inside class body are inline and are hidden from non-ADL lookup
  friend X operator+(X lhs,           // passing lhs by value helps optimize chained a+b+c
                     const X& rhs)    // otherwise, both parameters may be const references
  {
    lhs += rhs;                       // reuse compound assignment
    return lhs;                       // return the result by value (uses move constructor)
  }
};

I have 2 questions regarding this:

  1. How are chained a + b + c expressions optimized by this?
  2. Assuming X also overrides the copy and move assignment operators and constructors, would a statement like x = a + b produce any copying? Why or why not?
like image 231
Diggs Avatar asked Oct 16 '25 16:10

Diggs


1 Answers

I don't know whether this had that example-writer in mind, but have some explanation. Consider what happens in this code:

X a, b, c;
X d = a + b + c;

Here, first, a + b is evaluated basically as operator+(operator+(a, b), c). Note that operator+(a, b) is an rvalue, and therefore, lhs can, in the outer application of operator+, be initialized by move-constructor.

An alternative how to implement operator+ in terms of operator+= is as follows:

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

Note that you need to create a temporary since you need an object to apply operator+= on. With this solution, both applications of operator+ in operator+(operator+(a, b), c) involves copy-constructor.

Live demo: https://godbolt.org/z/5Dq7jF

Of course, you can add a second version for rvalues as follows:

friend X operator+(X&& lhs, const X& rhs) 
{    
   lhs += rhs;
   return std::move(lhs);
}

But this requires much more typing than the original value-passing version.


Generally, passing by value is frequently used in situations where you want to unify overloads for lvalues and rvalues; for example, look for unified assignment operator.

like image 122
Daniel Langr Avatar answered Oct 18 '25 05:10

Daniel Langr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!