To be friend or not to be friend

Hi, recently I'm writing classes/templates for polynomials and matrices. There seems to be several ways in which I can overload the "same" operator. I wonder which choice is the best (if there is one) and the reason behind.

This will be long but it really affects my future habit/pratise...so bear with me for the time being :P perhaps I could something that I really what a clear explanation on?

Let's begin with the operator+.
1
2
3
4
5
6
7
class Foo
{
   public:
     // I have two choices:
     Foo operator+ (const Foo&) const;
     friend Foo operator+ (const Foo&, const Foo&);
};

What are the significant differences in terms of performance (such as the minimum amount of temporary objects/variables are created in scope), or in terms of ease of management of code in the future?

Another example, a bit different:
1
2
3
4
5
6
class Foo
{
   public:
     Foo operator* (const double&) const;  // well, a side-question: double or double& ?
     friend Foo operator* (const double&, const Foo&);
};

This is not actually a "choice" because I need the second one to allow for something like
1
2
3
double k = 1.23;
Foo foo(/*blablabla*/);
k * foo;
, but back to the point, if I choose the second one in the previous example, I'd better write line 4 as friend Foo operator* (const Foo&, const double&);.

This maybe just my thought, but to be brief, do these operators conventionally have a this pointer?


There is another issue not really sticking to the topic, let's assume that I choose not to friend the operators.

For the operators like operator+ and operator+=, which should call which, again speaking in terms of performance?:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Here, assume that the corresponding ctors and operators are well-defined
// operator+ calls operator+=
Foo Foo::operator+ (const Foo& rhs) const
{
   Foo result(*this);
   result += rhs;
   return result;
}

// operator+= calls operator+
Foo& Foo::operator+= (const Foo& rhs)
{
   *this = *this + rhs;  // Hmm...is this line good?
   return *this;
}



Thank you for reading :) and thank you in advance for any explanation given.
Last edited on

[ [ + operator ] ]
What are the significant differences in terms of performance


Absolutely nothing. The two are identical apart from syntax. I generally prefer the member function approach (as opposed to the friend function) because writing global functions when I don't need to makes me feel dirty, but it really doesn't matter.

a side-question: double or double& ?


For basic data types (like double), I pass by value (so double), the reason is that passing by reference requires an indirection which may be slower than a variable copy.

For large objects (like classes), you're better off passing by reference because copying can be expensive.

But for a small function like this it doesn't matter whether your use double or double&, because the compiler will probably keep the value in a register anyway.


but back to the point, if I choose the second one in the previous example, I'd better write line 4 as friend Foo operator* (const Foo&, const double&);.


That's completely up to you. I generally do this:

1
2
3
4
5
6
7
8
class Foo
{
public:
  Foo operator + (const Foo& rhs)  {  /* put actual code here */  } // "meat" function
  Foo operator + (double rhs)    {  return *this + Foo(rhs);  }  // calls the "meat"
};

Foo operator + (double lhs, const Foo& rhs) { return Foo(lhs) + rhs; } // calls the "meat" 


No need for friend declarations, and only have to write the "meat" code once.

But again it's all personal preference. If you'd rather make them all global/friend functions, you can. It's just a matter of style. It does not impact performance at all.

do these operators conventionally have a this pointer?


The member functions do. The global/friend functions do not.

For the operators like operator+ and operator+=, which should call which, again speaking in terms of performance?:


+ should call +=

 
*this = *this + rhs;  // Hmm...is this line good? 


This creates a temporary object, reassigns it to *this, then destroys it. Creation of the temporary object was wholley unnecessary. It's better to avoid it:

1
2
3
4
5
6
7
8
9
10
11
Foo& Foo::operator += (const Foo& rhs)
{
  // no object copy
  data += rhs.data;
  return *this;
}

Foo Foo::operator + (const Foo& rhs)
{
  return Foo(*this) += rhs;  // calls +=
}


This might not be completely ideal in all situations, though. There may be instances where you might want to write code for + and += separately instead of having one call the other.
Last edited on
Now things are clear, thanks Disch :)
Topic archived. No new replies allowed.