On Monday, 21 April 2014 at 12:45:12 UTC, Steven Schveighoffer
wrote:
[...]
Reasons off the top of my head not to make them module
functions:
1. You can import individual symbols from modules. i.e.:
import mymodule: MyType;
If a large portion of your API is module-level functions, this
means you have to either import the whole module, or the
individual methods you plan to use.
Based on this, combined with your points 6 and 3 further down --
the second number 3, that is :) -- we can make the following
guideline:
Methods which are central to the class' usage, and which are
therefore likely to be used often, should be member functions,
while auxiliary functions and convenience functions should be
non-members.
The same thing was stated earlier in this thread, in different
words, and I guess it is the rule most of us use already.
However, this is the first non-subjective rationale I've seen for
it so far. Awesome!
2. You can get delegates to methods. You cannot get delegates
to module functions, even if they are UFCS compatible.
This is an excellent point. I would never have thought of that.
3. There is zero chance of a conflict with another type's
similarly named method.
How? If you have the following functions:
void foo(A a);
void foo(B b);
and you write
foo(new B);
there is also zero chance of conflict -- even if B happens to be
a subclass of A, since the most specialised function is always
called.
4. It enforces the "method call" syntax. I.e. you cannot use
foo(obj) call. This may be important for readability.
Some would argue that giving users the choice between typing
foo(obj) and obj.foo() is a Good Thing, because it doesn't impose
your preferences on them. I'm not going to do that, though. ;)
5. You can only use operator overloads via methods. D is
different in this respect from C++.
True. Operator overloads fall in the same category as virtuals
and interface functions, i.e., the ones that *cannot* be
non-members.
[...]
Reasons to make them module functions:
1. You have more than one object in the same file which
implements the method identically via duck typing.
2. You want to change how the 'this' type is passed -- in other
words, you want to pass a struct by value or by pointer instead
of by ref.
3. The complement to #1 in the 'against' list -- you want your
module-level API to be selectively enabled!
4. Of course, if you are actually implementing in a different
module, Scott Meyers' reasoning applies there.
All very good points. This is exactly what I was looking for,
thanks!