"Prefer writing nonmember nonfriend funcs" - when to stop?

Started by
17 comments, last by mrbastard 18 years, 4 months ago
found it. a very good read thoughout!

- simon
Advertisement
http://www.artima.com/intv/goldilocks4.html
http://www.artima.com/intv/mincomp.html

scott meyer's views on this topic. very recommended.
What about a Length() function for a vector. Should it be a member function or not? According to this rule, it should not because it does not affect the invariant and can be accomplished using other functions. By the same principal it could be argued that a Normalize() function should not be a member function.

The principle also implies that any class with no invariant (having all public data) should have no member functions.

I would rewrite it to say "Beyond expressing the fundamental characteristics and behavior of the class, prefer writing nonmember nonfriend functions wherever possible."
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
i'd say the length() or size() thing of a container is an exception to this rule. stroustrup does make exceptions from this rule when performance is of high concern. you'd want to complexity of size() or length() type functions to be O(1). that is not possible with the standard library design of a non-member function taking a range of iterators.
I think some interesting new points to this debate come about from considering the way modern graphical IDEs operate, particularly in what they usually call "IntelliSense." It can be quite handy to be able to have a list of all a type's related functionality pop up after typing in its accessor, but that only works in the case of member functions. While it would be possible for the IDE manufacturers to add related utility functions to such lists, there are a number of reasons I can think of off the top of my head that would make it impractical. Another related concern is with the object browsers these same IDEs provide.

In order to play devil's advocate, I'd say that the decision really comes down to aesthetics. For some applications, it makes sense to abstract a certain set of operations away from the underlying types, especially when those operations are going to be applied for a large family of related types. Algorithms in STL are a good example of this. On the other hand, sometimes separating implementations like this doesn't really do anything for you other than clutter your namespace. What I'm thinking here is when you want to implement some function that's strongly logically related to the concept that your class encapsulates.

Take, for example, the path class in the Boost filesystem library. This class is used to represent filesystem paths in a platform independent manner. It provides a method for retrieving the path in its entirety, as well as exposing an iterator for examining the individual elements of the path. Given this limited interface, it would be perfectly possible to implement extractors for specific path elements in non-member functions (eg. get_root( path ), get_leaf( path )). In this case, the library authors decided (and rightly so), to include these accessors directly in the interface. Even if you ignore the possibility of performance considerations, the logical relation between a path and its root/leaf is strong enough that it's simply more elegant this way.
This is a GotW in which that principle is discussed. I'm not sure if its the one the OP mentions or not.

And length() for vectors should obviously be a non-member, non-friend function. Vector's already have a member size(), adding length() at this point would be needlessly redundant :P

CM
Quote:Original post by Anonymous Poster
i'd say the length() or size() thing of a container is an exception to this rule...

Quote:Original post by Conner McCloud
... Vector's already have a member size(), adding length() at this point would be needlessly redundant :P

[headshake] I thought it was obvious that I was talking about a vector in the mathematical sense, as in a 3D vector. But now that you mention it, it would follow that std::vector<>::size() should be a non-member non-friend function.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
I'd like to thank everyone again for your thought provoking responses. Discussion like this is just what I need to weigh up the pros and cons in the general case.

Ap#1: I agree the STL is a good example of this (in places), especially the algorithms. As it goes, I'm not actually writing a container but a set of functionality that I'm trying to make container agnostic - like the STL algorithms (though it's not that aspect in particular that's caused this question). I can also see what you're saying about the example I gave, though as I'm working in C++ and C++ is a language that does that for me it seems silly not to use it.

Zahlman: "C++ is a multi-paradigm language" should be my new mantra. It'd save a lot of worrying about coding style. I'm going to rethink the design for a while longer to consider what's been brought up here and then I'll post the code for critique, if you're still happy to offer suggestions. The code I'm writing is quite closely related to what you're saying about iterators and STL-style algorithms. I'll start another thread, as this one might bring in some more general discussion.

Morbo: Thanks for that. While it's basic stuff, it's not how I'd been looking at this, and you're absolutely right that that's the context it needs to be seen in.

Simon: Thanks for the links. The Stroustrup one was great. One thing he says sums up nicely:

"My rule of thumb is that you should have a real class with an interface and a hidden representation if and only if you can consider an invariant for the class."

John Bolton: you and Shinkage both make strong arguments that there's no need to take Sutter's guideline to extremes.

Shinkage: msvc.net intellisense will do it by namespace as well, at least with visual assist. Means a more verbose syntax or deleting the namespace name after selecting the function though I suppose, which could probably be automated by checking what namespaces are in the current scope.

Conner McCloud: Yeah, that's the one.
[size="1"]

This topic is closed to new replies.

Advertisement