Why Can Some Operators Only Be Overloaded as Member Functions, Other as Friend Functions and the Rest of Them as Both

Why must (), [], ->, and = be overloaded only as member functions?

I think this is most likely why that portion of the standard was written that way.

but if it is not forbidden, the friend version would never be called, in my testing code ,when

 Complex operator+(const Complex &other);

is defined as private, the compiler would
give error message

‘Complex Complex::operator+(const Complex&)’ is private
Complex Complex::operator+(const Complex &other)

instead of using the friend version

refer to Why cannot a non-member function be used for overloading the assignment operator?

Because the default operator= provided by the compiler (the memberwise copy one) would always take precedence. I.e. your friend operator= would never be called.

(If the assignment was performed inside a class method, because of the lookup rules, the member function (in this case generated by the compiler) would take precedence)

I try to using operator + for test. it prove the precedence

it outputs:

member function called
7+11i

testing code:

#include<iostream>
using namespace std;
class Complex
{
public:
Complex(int real, int imag);
Complex(void);
~Complex(void);

Complex &Add(const Complex &other);

void Display() const;

Complex operator+(const Complex &other);

friend Complex operator+(const Complex &c1, const Complex &c2);

private:
int real_;
int imag_;
};
Complex::Complex(int real, int imag): imag_(imag), real_(real)
{

}
Complex::Complex(void)
{
}

Complex::~Complex(void)
{
}

Complex &Complex::Add(const Complex &other)
{
real_ += other.real_;
imag_ += other.imag_;
return *this;
}

void Complex::Display() const
{
cout << real_ << "+" << imag_ << "i" << endl;
}

Complex Complex::operator+(const Complex &other)
{
int r = real_ + other.real_;
int i = imag_ + other.imag_;
std::cout << "member function called"<< std::endl;
return Complex(r, i);
}

Complex operator+(const Complex &c1, const Complex &c2)
{
int r = c1.real_ + c2.real_;
int i = c1.imag_ + c2.imag_;
std::cout << "friend function called"<<std::endl;
return Complex(r, i);
}

int main(void)
{
Complex c1(3, 5);
Complex c2(4, 6);

Complex c3 = c1 + c2;

c3.Display();

return 0;
}

Overload operators as member function or non-member (friend) function?

Each operator has its own considerations. For example, the << operator (when used for stream output, not bit shifting) gets an ostream as its first parameter, so it can't be a member of your class. If you're implementing the addition operator, you'll probably want to benefit from automatic type conversions on both sides, therefore you'll go with a non-member as well, etc...

As for allowing specialization through inheritance, a common pattern is to implement a non-member operator in terms of a virtual member function (e.g. operator<< calls a virtual function print() on the object being passed).

Why are relational operators overloaded as non-member function in STL string?

Overloading them as non-member functions allows the LHS of the operator to be converted to type std::string. For example, the following does not work should operator== be a member:

std::string name = "foo";
if ("foo" == name)
// ...

That's because "foo".operator==(name) is not a valid expression.

Systematic approach of how to think about operator overloading

You got one thing wrong:

friend std::ostream& operator<<(std::ostream& os, Vector const& other);

You say this is written with two explicit arguments "because one of them is a stream." But actually it takes two explicit arguments because the friend keyword on the front implies this is a free function, not a member function. So it's covered by your third rule, and your second rule should be deleted entirely.

friend vs member functions in Operator Overloading C++

The choice isn't "member or friend" but "member or non-member".

(Friendship is frequently overused, and usually taught much too early in schools.)

This is because you can always add a public member function that a free function can call.

For instance:

class A
{
public:
explicit A(int y) : x(y) {}
A plus(const A& y) const { return A{x + y.x}; }
private:
int x;
};

A operator+(const A& lhs, const A& rhs) { return lhs.plus(rhs); }

As for how to choose: if the operator doesn't take an instance of the class as its left-hand operand, it must be a free function, otherwise it's pretty much a matter of personal taste (or coding standards if you're not alone).

Example:

// Can't be a member because the int is on the left.
A operator+ (int x, const A& a) { return A{x} + a; }

For operators that have a corresponding mutating operator (like + and +=), it's common to do the mutating operator as a member and the other as a non-member:

class B
{
public:
explicit B(int y) : x(y) {}
B& operator+= (const B& y) { x += y.x; return *this; }
private:
int x;
};

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

but you can spell this out too, of course:

class C
{
public:
explicit C(int y) : x(y) {}
C& add(const C& y) { x += y.x; return *this; }
private:
int x;
};

C& operator+=(C& lhs, const C& rhs) { return lhs.add(rhs); }
C operator+(C lhs, const C& rhs) { return lhs += rhs; }

Declaring = and [] operators for a class on the header file, must be a nonstatic member function error

@R Sahu's link was useful, showing that [] and = can no be declared as non-member, but it didn't really explain why.
@Baum mit aguen's link cleared some other questions too.
(Thanks for the information)

So, I adjusted my code to this new information as follows:

Block.h

class Block
{
public:
//...
coords* operator[](size_t);

Block operator=(Block);
//...
};

Block.cpp

//...
coords* Block::operator[](size_t index)
{
if(index >= 0 && index < block.size())
return &block.at(index);
coords *tmp = new coords(-1, -1);
return tmp;
}

Block Block::operator=(Block b2)
{
block.empty();
block.reserve(b2.block.size());
append(b2.block);
return *this;
}
//...

This way you can call *(*b1)[0] = c1; being Block* b1 and coords c1.

The friend modifier is useful for other types of implementations, although I only realized after that the implementation of inline had to be done in the header file, not on the cpp.
Block.h

class Block
{
public:
//...
friend std::ostream& operator<<(std::ostream&, const Block&);
friend std::ostream& operator<<(std::ostream&, Block&);
//...
};

inline std::ostream& operator<<(std::ostream& out, const Block& b)
{
// do something
return out;
};

inline std::ostream& operator<<(std::ostream& out, Block& b)
{
// do something
return out;
};

In this case, you need to pass the "this" parameter has to be passed to the function also as these are non-member functions and should be implemented outside the class, in the header file.

I hope this helps, good coding everyone.

Overloading member access operators ->, .*

->

This is the only really tricky one. It must be a nonstatic member function, and it takes no arguments. The return value is used to perform the member lookup.

If the return value is another object of class type, not a pointer, then the subsequent member lookup is also handled by an operator-> function. This is called the "drill-down behavior." The language chains together the operator-> calls until the last one returns a pointer.

struct client
{ int a; };

struct proxy {
client *target;
client *operator->() const
{ return target; }
};

struct proxy2 {
proxy *target;
proxy &operator->() const
{ return * target; }
};

void f() {
client x = { 3 };
proxy y = { & x };
proxy2 z = { & y };

std::cout << x.a << y->a << z->a; // print "333"
}

->*

This one is only tricky in that there is nothing special about it. The non-overloaded version requires an object of pointer to class type on the left-hand side and an object of pointer to member type on the right. But when you overload it, you can take any arguments you like and return anything you want. It doesn't even have to be a nonstatic member.

In other words, this one is just a normal binary operator like +, -, and /. See also: Are free operator->* overloads evil?

.* and .

These cannot be overloaded. There is already a built-in meaning when the left-hand side is of class type. Perhaps it would make a little sense to be able to define them for a pointer on the left-hand side, but the language design committee decided that would be more confusing than useful.

Overloading ->, ->*, ., and .* can only fill in cases where an expression would be undefined, it can never change the meaning of an expression that would be valid with no overloading.

How to use both member function and friend function operator overloading in same class

Most operators can be overloaded either as member or nonmember function. Some operators (= [] () ->) can be overloaded only as members. But you cannot have any oeprator both as a member and nonmember, specifically because it is unclear how to pick either one of them.

What the assignment wants is probably:

  • you overload prefix ++ as a member and the postfix ++ as a nonmember (in your case, also friend)
  • create two separate programs: in one, those operators are overloaded as members, in another - as nonmembers


Related Topics



Leave a reply



Submit