To be honest, I think the only reasons to make such a decision are syntactic convenience and tradition. I'll explain why by showing what are (not) the differences between the two and how these differences matter when making a decision.
What differences are there between non-member friend functions and public member functions? Not much. After all, a member function is just a regular function with a hidden this
parameter and access to the class's private members.
// what is the difference between the two inv functions?
// --- our code ---
struct matrix1x1 { // this one is simple :P
private:
double x;
public:
//... blah blah
void inv() { x = 1/x; }
friend void inv(matrix1x1& self) { self.x = 1/self.x; }
};
matrix1x1 a;
// --- client code ---
// pretty much just this:
a.inv();
// vs this:
inv(a);
void lets_try_to_break_encapsulation(matrix1x1& thingy) {
thingy.x = 42; // oops, error. Nope, still encapsulated.
}
They both provide the same functionality, and in no way do they change what other functions can do. The same internals get exposed to the outside world: there's no difference in terms of encapsulation. There's absolutely nothing that other functions can do differently because there's a friend function that modifies private state.
In fact, one could write most classes with most functions as non-member friend functions (virtual functions and some overloaded operators must be members) providing the exact same amount of encapsulation: users cannot write any other friend function without modifying the class, and no function other than the friend functions can access private members. Why don't we do that? Because it would be against the style of 99.99% of C++ programmers and there's no great advantage to be taken from it.
The differences lie in the nature of the functions and the way you call them. Being a member function means you can get a pointer to member function from it, and being a non-member function means you can get a function pointer to it. But that's rarely relevant (especially with generic function wrappers like std::function
around).
The remaining difference is syntactic. The designers of the D language decided to just unify the whole thing and say that you can call a member function directly by passing it an object like inv(a)
, and call a free function as a member of its first argument, like a.inv()
. And no class suddenly got badly encapsulated because of that or anything.1
To address the particular example in the question, should inv
be a member or a non-member? I'd probably make it a member, for the familarity argument I outlined above. Non-stylistically, it doesn't make a difference.
1. This is unlikely to happen in C++ because at this point it would be a breaking change, for no substantial benefit. It would, for an extreme example, break the matrix1x1
class I wrote above because it makes both calls ambiguous.