In Java there are no free functions, which simplifies lookup rules and code organization. Many C++ style guides have adopted the “only classes” style, prohibiting free functions. But C++ is not Java.
First things first: This is no rant against Java. I am not a language zealot who thinks that “There Is Only One Language” and ignorantly ridicules all other languages. I think of both Java and C++ as different tools, suitable to tackle different problems.
The Benefits of “Everything in a Class”
The Java approach makes things easy. If every function is inside a class, then the compiler and the reader have a clear context for every piece of code.
This makes lookup rules very simple, since any nonqualified function call has to be a method of the current class or one of its base classes. On qualified call the object or, in the case of static methods, the class is provided in the code, so lookup is easily simple.
Code organization is easily simple: You either already have a class where a function you have to write clearly belongs to, or you create a new class. Since each class usually has its own source file in Java, you immediately know where to put the function.
C++ lookup rules
In C++, if you do not stick to “Everything in a Class”, lookup rules get fairly complicated. Qualified function calls behave similar to Java. But with unqualified function calls you can get lost quickly.
The compiler will look up unqualified function calls in different places. At first it will look for a matching name in the same scope, i.e. the same class and its base classes for methods or the same namespace. Then it will go into the next enclosing scope, i.e. outer classes or namespaces, until it hits the global namespace.
But it does not sop there. Enters argument dependent lookup (ADL). If the function has arguments, it looks into the namespaces of the types and base types of those arguments, if there is a free function that has a matching name.
And the outer namespaces of those classes.
This can get very complicated very quickly. But does that justify the “Everything in a Class” rule?
Drawbacks of the rule
Banning free functions has several implications on how code gets structured and restricts the use of language features. I will list a few of them, but there are more.
Artificial classes: Having to put everything in a class means you have to create artificial helper classes for functions that don’t belong into an existing class. Such classes often feel unnatural and irritating.
Operator overloading: Many operators should be or even have to be free functions. Being strict about the rule means to cripple one of the language’s key abilities to design classes with a fluent and readable interface.
Readability: A call to a well named free function often is enough to know what is going on, even if the function does not belong to the class where the call appears. Having to make a qualified call with the name of some helper class hurts the fluent readability of code.
Fat interfaces and scope creep: In order to avoid artificial helper classes that due to other coding style rules would have to go into separate files, programmers sometimes tend to put into classes that are only closely related to the function itself, thereby needlessly augmenting the interface of that class.
But we can’t just drop the rule, can we?
No, we should not simply drop it. There is a reason it is part of Java. But since C++ is different, we should not blindly copy it. We should replace it by sensibly organizing our code.
Keep closely related functions that do not need access to private members as free functions in the same file.
Helper functions and operators that clearly belong to a class should be declared in the header that contains the class definition.
If you have member functions that don’t need access to the class’ private or protected members, consider if they can be made free functions to decrease coupling.
Keep free functions that are less closely related in the same namespace as the classes they work on.
Functions that don’t directly belong to a class but work with objects of the class usually belong to the same group of functionality. Therefore they should be part of the same namespace.
In other words, don’t rely on ADL too much. More generally speaking:
Organize your code in a way that represents the dependencies of the classes and functions.
Of course this does not mean you should drop helper classes altogether. Use them if it makes sense, e.g. if their names provide a context that otherwise would be missing.
Choose disciplined and well-thought organization of your code over the dogmatic and overly restrictive “Everything in a Class” rule.