string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable DeclarationsOriginally posted as totw/119 on 2016-07-14
By Thomas Köppe ([email protected])
This tip gives a simple, robust recipe for writing using-declarations and
namespace aliases in .cc
files that avoids subtle pitfalls.
Before we dive into the details, here is an example of the recipe in action:
namespace example {
namespace makers {
namespace {
using ::otherlib::BazBuilder;
using ::mylib::BarFactory;
namespace abc = ::applied::bitfiddling::concepts;
// Private helper code here.
} // namespace
// Interface implementation code here.
} // namespace makers
} // namespace example
Remember that everything in this tip applies only to .cc
files, since you
should never put convenience aliases in header
files. Such aliases
are a convenience for the implementer (and the reader of an implementation), not
an exported facility. (Names that are part of the exported API may of course
be declared in headers.)
(Remember that you can always have a local namespace alias or using-declaration in a block scope, which can be handy in header-only libraries.)
C++ organizes names into namespaces. This crucial facility allows code bases
to scale by keeping ownership of names local avoiding name collisions in other
scopes. However, namespaces impose a certain cosmetic burden, since qualified
names (foo::Bar
) are often long and quickly become clutter. We often find it
convenient to use unqualified names (Bar
). Additionally, we may wish to
introduce a namespace alias for a long but frequently used namespace: namespace
eu = example::v1::util;
We will collectively call using-declarations
and namespace aliases just aliases in this tip.
The purpose of a namespace is to help code authors avoid name collisions, both at the point of name lookup and at the point of linking. Aliases can potentially undermine the protection afforded by namespaces. The problem has two separate aspects: the scope of the alias, and the use of relative qualifiers.
The scope at which you place an alias can have subtle effects on code maintainability. Consider the following two variants:
using ::foo::Quz;
namespace example {
namespace util {
using ::foo::Bar;
It appears that both using-declarations are effective at making the
names Bar
and Quz
available for unqualified lookup inside our working
namespace ::example::util
. For Bar
, everything is working as expected,
provided there is no other declaration of Bar
inside namespace
::example::util
. But this is your namespace, so it is in your power to control
this.
On the other hand, if a header is later included that declares a global name
Quz
, then the first using-declaration becomes ill-formed, as it attempts to
redeclare the name Quz
. And if another header declares ::example::Quz
or
::example::util::Quz
, then unqualified lookup will find that name rather
than your alias.
This brittleness can be avoided if you do not add names to namespaces that you do not own (which includes the global namespace). By placing the aliases inside your own namespace, the unqualified lookup finds your alias first and never continues to search containing namespaces.
More generally, the closer a declaration is to the point of use, the smaller is
the set of scopes that can break your code. At the worst end of our example is
Quz
, which can be broken by anyone; Bar
can only be broken by other code in
::example::util
, and a name that is declared and used inside an unnamed
namespace cannot be broken by any other scope. See Unnamed
Namespaces for an example.
A using-declaration of the form using foo::Bar
seems innocuous, but it is in
fact ambiguous. The problem is that it is safe to rely on the existence of
names in a namespace, but it is not safe to rely on the absence of names.
Consider this code:
namespace example {
namespace util {
using foo::Bar;
It is perhaps the author’s intention to use the name ::foo::Bar
.
However, this can break, because the code is relying on the existence of
::foo::Bar
and also on the non-existence of namespaces ::example::foo
and
::example::util::foo
. This brittleness can be avoided by qualifying the used
name fully: using ::foo::Bar
.
The only time that a relative name is unambiguous and cannot possibly be broken by outside declarations is if it refers to a name that is already inside your current namespace:
namespace example {
namespace util {
namespace internal {
struct Params { /* ... */ };
} // namespace internal
using internal::Params; // OK, same as ::example::util::internal::Params
This follows the same logic that we discussed in the previous section.
What if a name lives in a sibling namespace, such as ::example::tools::Thing
?
You can say either tools::Thing
or ::example::tools::Thing
. The fully
qualified name is always correct, but it may also be appropriate to use the
relative name. Use your own judgment.
A cheap way to avoid a lot of these problems is not to use namespaces in your
project that are the same as popular top-level namespaces (such as util
); the
Style Guide recommends this practice explicitly.
The following code shows examples of both failure modes.
helper.h:
namespace bar {
namespace foo {
// ...
} // namespace foo
} // namespace bar
some_feature.h:
extern int f;
Your code:
#include "helper.h"
#include "some_feature.h"
namespace foo {
void f();
} // namespace foo
// Failure mode #1: Alias at a bad scope.
using foo::f; // Error: redeclaration (because of "f" declared in some_feature.h)
namespace bar {
// Failure mode #2: Alias badly qualified.
using foo::f; // Error: No "f" in namespace ::bar::foo (because that namespace was declared in helper.h)
// The recommended way, robust in the face of unrelated declarations:
using ::foo::f; // OK
void UseCase() { f(); }
} // namespace bar
A using-declaration placed in an unnamed namespace can be accessed from the enclosing namespace and vice versa. If you already have an unnamed namespace at the top of the file, prefer putting all aliases there. From within that unnamed namespace, you gain an extra little bit of robustness against clashing with something declared in the enclosing namespace.
namespace example {
namespace util {
namespace {
// Put all using-declarations in here. Don't spread them over the file.
using ::foo::Bar;
using ::foo::Quz;
// In here, Bar and Quz refer inalienably to your aliases.
} // namespace
// Can use both Bar and Quz here too. (But don't declare any entities called Bar or Quz yourself now.)
So far we have been talking about local aliases for distant names. But what if
we want to use names directly and not create aliases at all? Should we say
util::Status
or ::util::Status
?
There is no obvious answer. Unlike the alias declarations that we have been
discussing until now, which appear at the top of the file far away from the
actual code, the direct use of names affects the local readability of your code.
While it is true that relatively qualified names may break in the future, there
is a significant cost for using absolute qualifications. The visual clutter
caused by the leading ::
may well be distracting and not worth the added
robustness. In this case, use your own judgment to decide which style you
prefer. See TotW 130.
All credit is due to Roman Perepelitsa ([email protected]) who originally suggested this style in a mailing list discussion and who contributed numerous corrections and punchlines. However, all errors are mine.