Scoping
Namespaces
With few exceptions, place code in a namespace. Namespaces should have unique names based on the project name, and possibly its path. Do not use using-directives (e.g. using namespace foo
). Do not use inline namespaces.
Definition
Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope.
Pros
Namespaces provide a method for preventing name conflicts in large programs while allowing most code to use reasonably short names.
For example, if two different projects have a class Foo in the global scope, these symbols may collide at compile time or at runtime. If each project places their code in a namespace, project1::Foo
and project2::Foo
are now distinct symbols that do not collide, and code within each project’s namespace can continue to refer to Foo
without the prefix.
Inline namespaces automatically place their names in the enclosing scope. Consider the following snippet, for example:
namespace outer {inline namespace inner {void foo();} // namespace inner} // namespace outer
The expressions outer::inner::foo()
and outer::foo()
are interchangeable. Inline namespaces are primarily intended for ABI compatibility across versions.
Cons
Namespaces can be confusing, because they complicate the mechanics of figuring out what definition a name refers to.
Inline namespaces, in particular, can be confusing because names aren’t actually restricted to the namespace where they are declared. They are only useful as part of some larger versioning policy.
In some contexts, it’s necessary to repeatedly refer to symbols by their fully-qualified names. For deeply-nested namespaces, this can add a lot of clutter.
Unnamed Namespaces and Static Variables
When definitions in a .cc
file do not need to be referenced outside that file, place them in an unnamed namespace or declare them static
. Do not use either of these constructs in .h
files.
Definition
All declarations can be given internal linkage by placing them in unnamed namespaces. Functions and variables can also be given internal linkage by declaring them static
. This means that anything you’re declaring can’t be accessed from another file. If a different file declares something with the same name, then the two entities are completely independent.
Decision
Use of internal linkage in .cc
files is encouraged for all code that does not need to be referenced elsewhere. Do not use internal linkage in .h
files.
Format unnamed namespaces like named namespaces. In the terminating comment, leave the namespace name empty:
namespace {...}
Nonmember, Static Member, and Global Functions
Prefer placing nonmember functions in a namespace; use completely global functions rarely. Do not use a class simply to group static functions. Static methods of a class should generally be closely related to instances of the class or the class’s static data.
Pros
Nonmember and static member functions can be useful in some situations. Putting nonmember functions in a namespace avoids polluting the global namespace.
Cons
Nonmember and static member functions may make more sense as members of a new class, especially if they access external resources or have significant dependencies.
Decision
Sometimes it is useful to define a function not bound to a class instance. Such a function can be either a static member or a nonmember function. Nonmember functions should not depend on external variables, and should nearly always exist in a namespace. Do not create classes only to group static member functions; this is no different than just giving the function names a common prefix, and such grouping is usually unnecessary anyway.
Local Variables
Place a function’s variables in the narrowest scope possible, and initialize variables in the declaration.
C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.:
int i;i = f(); // Bad -- initialization separate from declaration.
int j = g(); // Good -- declaration has initialization.
std::vector<int> v;v.push_back(1); // Prefer initializing using brace initialization.v.push_back(2);
std::vector<int> v = {1, 2}; // Good -- v starts initialized.
Variables needed for if
, while
and for
statements should normally be declared within those statements, so that such variables are confined to those scopes. E.g.:
while (const char* p = strchr(str, '/')) str = p + 1;
There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.
// Inefficient implementation:for (int i = 0; i < 1000000; ++i) {Foo f; // My ctor and dtor get called 1000000 times each.f.DoSomething(i);}
It may be more efficient to declare such a variable used in a loop outside that loop:
Foo f; // My ctor and dtor get called once each.for (int i = 0; i < 1000000; ++i) {f.DoSomething(i);}