4

I recently got introduced the design of generic programming libraries like STL, boost::graph, boost PropertyMaps http://www.boost.org/doc/libs/1_54_0/libs/property_map/doc/property_map.html

What is the rationale behind using free functions like get(PropertyMap, key) over member functions like PropertyMap.get(key)?

I understand that the most generic form of these functions are defined in the "boost" namespace. Suppose I define a new PropertyMap in my namespace "project", what is the best place to define it's corresponding "get" function? "boost" or "project"

Vikas
  • 2,220
  • 1
  • 15
  • 12

4 Answers4

6

There are two different reasons:

  • Better Encapsulation: by minimizing the number of functions having access to the class attributes, you improve encapsulation.

  • Extensibility: in C++, a namespace definition is open (you can add to it) whilst a class definition is closed; therefore you can add free functions, but not member functions.

While encapsulation is more a matter of taste, extensibility is extremely important in generic programming. You do not want to have to abandon a 3rd party type just because it lacks the one method you need... and of course some types (such as the built-in types or standard library types) cannot be extended at all.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
4

Free functions are less tightly coupled to a particular implementation (since they rely only on the public interface of the container). This allows for greater flexibility and ease of maintenance.

You'll note that when a particular implementation matters (for example std::map::find) the standard library uses member functions.

Scott Meyers has a set of rules that work pretty well for deciding how to create your functions: http://cpptips.com/nmemfunc_encap

Mark B
  • 95,107
  • 10
  • 109
  • 188
3

The main motivation is that preferring nonmember nonfriend functions helps keep classes as concise as possible. See Herb Sutter's article here: http://www.gotw.ca/publications/mill02.htm.

This article also contains the answer to the other part of your question, where to put the corresponding get function. It's a feature of C++ called Argument Dependent Lookup (ADL). From Herb Sutter, who calls it Koenig lookup although this name is controversial (see comments below):

Koenig lookup says that, if you supply a function argument of class type, then to find the function name the compiler is required to look, not just in the usual places like the local scope, but also in the namespace (here NS) that contains the argument's type.

Here's an example:

namespace MyNamespace {
    class MyClass {... };
    void func(MyClass);
}

int main(int aArgc, char* aArgv[]) {
    MyNamespace::MyClass inst;
    func(inst);  // Ok, because Koenig says look in the argument's namespace for func
}

So in short, you just declare the get function in the same namespace as your class.

Be aware that this doesn't work for templated functions if you have to supply the template parameters explicitly -- see this post: https://stackoverflow.com/a/2953783/27130

Community
  • 1
  • 1
Nathan Monteleone
  • 5,430
  • 29
  • 43
  • I've been told that Andy Koenig gets very upset when people call this "Koenig Lookup", though I've never asked him personally. The correct term is ADL, or argument-dependent lookup. Unless I'm mistaken, Andy only proposed ADL for operators. The rest is an invention of the C++ standardization committee ... and causes no end of trouble. – Eric Niebler Jul 18 '13 at 07:37
2

The clue is in the question, I think - it makes them more generic. A free function like std::find() can be used with a large number of different containers, and is therefore generic - a member like std::map<T>.find() only works with std::map, and therefore isn't generic at all.

With good generic functions, you should be able to design your own containers that can interface with them, relieving you of the need to write your own methods within your container (and of the need to write your own free functions, obviously). It makes sense to provide a method when you have a container for which a particular operation can be implemented better in a way specific to that container than in a generic function (std::map's find() method being an obvious example).

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • "...a particular operation can be implemented better in a way specific to that container..." -- this actually sounds like a job for [partial] template specialization. – wjl Feb 16 '14 at 20:56