Tip of the Week #186: Prefer to Put Functions in the Unnamed Namespace
Originally posted as TotW #186 on November 5, 2020
By James Dennett and Jason Rennie
“Everything should be made as simple as possible, but no simpler.” ~ Roger
Sessions’s interpretation of Einstein
When adding a new function, default to making it a non-member function local to
.cc file where it is called. While there are valid reasons to make another
choice, consider writing it in an unnamed namespace (also known as an “anonymous
Writing a non-member in an unnamed namespace has benefits both by making
functions internal to a
.cc file (moving them out of header files) as well as
by making them non-members (moving them out of classes).
Benefits over functions declared in a header file include:
- Making it easy for a reader to look up the definition (since it’s in the
same file as usage and above the first usage).
- Placing the documentation, declaration, and definition in a single location
(versus two locations for functions which are declared in a header file).
- Isolating it from other source files, making it easier to refactor.
- Removing the need to worry about meaningful const as there is
no separate declaration.
- Keeping them in the same place as other implementation helpers for a
library, such as convenience aliases and local types. See
Tip of the Week #119: Using-declarations and Namespace Aliases.
- Providing side benefits, such as allowing a type to also be moved to an
unnamed namespace (if it is only referenced in a single source file).
Benefits over private methods include:
- Inputs and outputs are clear since they are (much more likely to be)
specified via arguments or the return value. Note that a method may read any
member variable and a non-const method may modify any non-const member.
Conversely, a non-member function may only read or modify according to its
interface (except for globals).
- The class API is simpler and shorter, and hence easier to read—unnecessary
private methods may make it difficult to find inheritance-related private
declarations or declarations after the class.
Reasons to Look Elsewhere
Sometimes a non-member local function does not make sense. For example:
- When the function is useful in multiple source files. Declaring it in a
header file allows re-use.
- When the function has complex interactions with an object or class. For
example, a function that reads from multiple fields and needs to modify
state in a way that can’t be handled naturally via a return value is likely
better written as a method. In particular, logic involving a mutex usually
belongs in a member function.
- When the function belongs as part of an class’s API.
static Non-Member Functions
Marking a non-member function as
static has essentially the same effect as
placing it inside an unnamed namespace in terms of isolating it from code in
other translation units. While unnamed namespaces do this in a uniform manner
that covers types as well as functions and objects, some people like to see
static written explicitly in the declaration of a function to show that it is
local to a translation unit without having to check for an enclosing unnamed
namespace. While this tip recommends using an unnamed namespace, using
can be a reasonable choice.
Parts of the style guide
point us in this direction, but they don’t go as far. For example:
- The unnamed namespaces section
encourages that definitions go in an unnamed namespace (or be declared
static), but doesn’t talk about private methods.
- The inputs and outputs section
encourages using return values, but doesn’t address modification of members
this is essentially an uber-input/output parameter.
- The local variables section
encourages minimizing variable scope, but doesn’t extend the idea to classes
File-local functions simplify dependencies and improve locality. Non-member
functions increase encapsulation, simplify class definitions, and make
dependencies more explicit. When writing a function, consider making it a
file-local non-member function, such as by putting it in an unnamed namespace of