C++ Member Functions vs. Free Functions
As a C++ programmer, you are probably familiar with the following design question: Should you implement a function as a class member or as a free function?
Not so sure about the answer? Well, let’s examine.
An Example
Here’s an example from the mesh processing library I’m working on. Let’s say you have a class SurfaceMesh
for representing polygon meshes and you want to add a function that reads a mesh from different file formats.
You basically have two obvious options.
Either you add a class member:
class SurfaceMesh {
...
void read(const std::filesystem::path& file);
}
Or you use a free function:
void read(SurfaceMesh& mesh, const std::filesystem::path& file);
Which option would you choose?
In the past, we used to have a member function, mostly for sake of convenience. It’s easy to discover, type, and remember. Problem solved?
SurfaceMesh mesh;
mesh.read("file.obj");
Well, not so fast. This might be the obvious solution, but it’s not necessarily the best way from a software design perspective.
In fact, there are very solid arguments for preferring free functions. Here are just a few:
- Loose Coupling: A free function is more loosely coupled to the class it is operating on. It only depends on the interface. This also enables generic functions being usable with different concrete classes.
- Encapsulation and Hiding: A free function promotes encapsulation and information hiding since it does not have access to the implementation details of the class.
- Flexibility and Extensibility: Adding another free function is cheap and easy and does not require modification of the class definition.
- Testing: A free function is generally easier to test due to increased independence. No hacks required to test those pesky private member functions.
And the list goes on. In fact, there is an excellent talk by Klaus Iglberger going through this question in detail. He basically argues that free functions are the preferable choice in most cases. Notably, free functions help to follow SOLID principles.
Along similar lines, Herb Sutter also recommends free functions in his C++ Coding Standards. Scott Meyers also favors free functions in Effective C++ item 23. He even came up with an algorithm to decide when to use a free function or a member function: Given a class C
and a function f
related to C
, use the following algorithm:
if (f needs to be virtual)
{
make f a member function of C;
}
else if (f is operator>> or operator<<)
{
make f a non-member function;
if (f needs access to non-public members of C)
{
make f a friend of C;
}
}
else if (f needs type conversions on its left-most argument)
{
make f a non-member function;
if (f needs access to non-public members of C)
{
make f a friend of C;
}
}
else
{
make f a member function of C;
}
Admittedly, there are some advantages of member functions as well. For some folks they just might be easier to use. In particular, you get better IDE support for auto-completion when typing something like mesh.
in the example above. There’s also a little less ambiguity since there’s at least one function argument less to worry about.
Conclusions
In general, preferring free functions seems like a very reasonable choice. I think the main counter-argument is usability and discovery. However, there also is a proposal by Bjarne Stroustrup and Herb Sutter for a unified function call syntax that would allow to simply write
mesh.read("file.stl");
if there is a function with the signature
read(Mesh mesh, const std::string& filename)
While this certainly would add some convenience, I’m not entirely sure it’s worth the added complexity. It’s not that C++ doesn’t already have enough of that!
Let me close by saying that in practice the choice is not always that obvious. There might be cases when you design an API around free functions only to later realize that some function does indeed needs access to the internals of the class. This has been exactly the case in the motivating example above, and so I resorted to a friend
function. 😕
References and Further Reading
- Free Your Functions by Klaus Iglberger
- C++ Coding Standards by Herb Sutter and Andrei Alexandrescu
- Effective C++ by Scott Meyers
- Unified Call Syntax Proposal by Bjarne Stroustrup and Herb Sutter