Where Should Non-Member Operator Overloads Be Placed

Where should non-member operator overloads be placed?

You should put the operator overload in the same namespace as your class.

This will allow the operator to be found during overload resolution using argument-dependent lookup (well, actually, since ostream is in namespace std, the overload overload would also be found if you put it in namespace std, but there is no reason to do that).

From the point of view of good design practices, the operator overload is more a part of your class's interface than the interface of ostream, so it belongs in the same namespace as your class (see also Herb Sutter's Namespaces and the Interface Principle).

From the point of view of writing standards-compliant and portable code, you can't put the operator overload into namespace std. While you can add template specializations for user-defined entities to namespace std, you can't add additional function overloads.

Calling non-member operator overloads

using std::operator<<;
operator<<(*this, v);

std is an associated namespace of *this anyway, so this doesn't introduce anything new into the overload set. Alternatively, define a namespace-scope operator<< taking some dummy type and pull that in with using.

operator overloading and non-member functions c++

Here is the addition operator outside of the class:

Complex operator+(const Complex& lhs, const Complex& rhs) {
//implement the math to add the two
return Complex(lhs.aGetValue() + rhs.aGetValue(),
lhs.bGetValue() + rhs.bGetValue());
}

Of course you will need to declare aGetValue() and bGetValue() as const:

double aGetValue() const {return a;}
double bGetValue() const {return b;}

Operator overloading : member function vs. non-member function?

If you define your operator overloaded function as member function, then the compiler translates expressions like s1 + s2 into s1.operator+(s2). That means, the operator overloaded member function gets invoked on the first operand. That is how member functions work!

But what if the first operand is not a class? There's a major problem if we want to overload an operator where the first operand is not a class type, rather say double. So you cannot write like this 10.0 + s2. However, you can write operator overloaded member function for expressions like s1 + 10.0.

To solve this ordering problem, we define operator overloaded function as friend IF it needs to access private members. Make it friend ONLY when it needs to access private members. Otherwise simply make it non-friend non-member function to improve encapsulation!

class Sample
{
public:
Sample operator + (const Sample& op2); //works with s1 + s2
Sample operator + (double op2); //works with s1 + 10.0

//Make it `friend` only when it needs to access private members.
//Otherwise simply make it **non-friend non-member** function.
friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

Read these :

A slight problem of ordering in operands

How Non-Member Functions Improve Encapsulation

Operator overload: Member vs. non-member when only same type objects can be involved

Because operator== has symmetric semantics for its LHS and RHS argument, the recommended approach is to always implement it as a non-member in terms of the public interface of its operands (or if private data is required, to declare it as a friend inside the class).

So

class Bla
{
public:
// complete interface to data required for comparison
auto first();
auto second();
// ... more
private:
// data goes here
};

bool operator==(Bla const& L, Bla const& R)
{
return
std::forward_as_tuple(L.first(), L.second() /*, ... */) ==
std::forward_as_tuple(R.first(), R.second() /*, ... */)
;
}

This way, implicit conversion to Bla are considered for both the L and R arguments (I'm not saying implicit conversions are a good idea, but if you have those, it's better to avoid surprises where they are only considered for the RHS argument).

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).

how to overload + and += operators as non-member functions?

In your example the operator + should either be made inline so that the linker knows that multiple obj files can contain the same function definition.

inline Complex operator+ (Complex a, Complex b) { return a += b; }

Or the header file should contain only the declaration

Complex operator+ (Complex a, Complex b);

and exactly one cpp file should contain the definition

Complex operator+ (Complex a, Complex b) { return a += b; }

Are non-member operator overloads (specifically operator==) broken in Cython?

Two things are broken about Cython's handling of non-member operators:

  1. Non-member operators defined in a .pxd file outside a class aren't correctly cimported to Cython (the workround is to do from something cimport *) and thus aren't used. This is what I think I said in my previous answer

  2. Non-member operators defined within a class with two arguments (as in the code you showed in unordered_map.pxd) aren't recognised by Cython and aren't used (despite being defined like that all over the C++ standard library wrappers included in Cython). At one point I tried to submit a patch for this but it was ignored. This is no longer true. Only point 1 now applies.

What does work is to tell Cython that it's a member operator (even if C++ implements it as a non-member operator). Therefore a simple patch to unordered_map.pxd would work. Note that I'm changing it to be defined with one argument and the C++ implicit this/self:

cdef extern from "<unordered_map>" namespace "std" nogil:
cdef cppclass unordered_map[T, U]:
# ... irrelevant stuff expunged ...
bint operator==(unordered_map&)

Alternatively, you can define it yourself before you need to use it (like you're doing currently) but as a template. This at least saves you having to define every specialization

cdef extern from "<unordered_map>" namespace "std":
bint operator==[R,S](unordered_map[R, S]&, unordered_map[R, S]&)

i.e. the statement in your question

(because Cython doesn't support generically templated functions, only generically templated classes and explicitly declared templates of functions)

isn't true.


It is all a bit of a mess though



Related Topics



Leave a reply



Submit