Discussion
Team LiB
Previous Section Next Section

Discussion

In short: You can and should use namespace using declarations and directives liberally in your implementation files after #include directives and feel good about it. Despite repeated assertions to the contrary, namespace using declarations and directives are not evil and they do not defeat the purpose of namespaces. Rather, they are what make namespaces usable.

Namespaces deliver the powerful advantage of unambiguous name management. Most of the time, different programmers don't choose the very same name for a type or function; but on the off chance that they do so, and that the two pieces of code end up being used together, having those names in separate namespaces prevents them from colliding. (We don't, after all, want the global namespace pollution experienced by default in languages like C.) In the rare case when there is such an actual ambiguity, calling code can explicitly qualify a name to say which one it wants. But the vast majority of the time there is no ambiguity: And that is why namespace using declarations and directives are what make namespaces usable, because they greatly reduce code clutter by freeing you from having to tediously qualify every name every time (which would be onerous, and frankly people just won't put up with it) and still letting you qualify names only in those rare cases when you need to resolve an actual ambiguity.

But using declarations and directives are for your coding convenience, and you shouldn't use them in a way that affects someone else's code. In particular, don't write them anywhere they could be followed by someone else's code: Specifically, don't write them in header files (which are meant to be included in an unbounded number of implementation files, and you shouldn't mess with the meaning of that other code) or before an #include (you really don't want to mess with the meaning of code in someone else's header).

Most people understand viscerally why a using directive (e.g., using namespace A;) causes pollution when it can affect code that follows and that isn't aware of it: Because it imports one namespace wholesale into another, including even those names that haven't been seen yet, it's fairly obvious that it can easily change the meaning of code that follows.

But here's the common trap: Many people think that using declarations issued at namespace level (for example, using N::Widget;) are safe. They are not. They are at least as dangerous, and in a subtler and more insidious way. Consider:



// snippet 1


namespace A {


 int f(double);


}





// snippet 2


namespace B {


  using A::f;


  void g();


}





// snippet 3


namespace A {


 int f(int);


}





// snippet 4


void B::g() {


  f(1);                         // which overload is called?


}



The dangerous thing happening here is that the using declaration takes a snapshot of whatever entities named f in namespace A have been seen by the time the using declaration is encountered. So, from within B, which overloads are visible depends on where these code snippets exist and in what order they are combined. (At this point, your internal "but order dependencies are evil!" klaxon should be blaring.) The second overload, f(int), would be a better match for the call f(1), but f(int) will be invisible to B::g if its declaration comes after the using declaration.

Consider two specific cases. First, let's say that snippets 1, 2, and 3 are in three distinct header files s1.h, s2.h , and s3.h, and snippet 4 in an implementation file s4.cpp that includes those header files to pull the relevant declarations. Then, we have an unfortunate phenomenon: The semantics of B::g depends on the order in which the headers were included in s4.cpp! In particular:

  • If s3.h comes before s2.h, B::g will call A::f(int).

  • Else if s1.h comes before s2.h, B::g will call A::f(double).

  • Else B::g won't compile at all.

At least in the preceding case, there's still one well-defined order, and the answer will be exactly one of the three listed alternatives.

But now it gets much worse: Let's instead say that snippets 1, 2, 3, and 4 are in four distinct header files s1.h, s2.h, s3.h, and s4.h. Now life is even more unfortunate: The semantics of B::g depends on the order in which the headers were included, not only in s4.h itself, but in any code that includes s4.h! In particular, an implementation file client_code.cpp might try to include the headers in any order:

  • If s3.h comes before s2.h, B::g will call A::f(int).

  • Else if s1.h comes before s2.h, B::g will call A::f(double).

  • Else B::g won't compile at all.

This is worse because two implementation files can include the headers in different orders. Consider what happens if client_code_1.cpp includes s1.h, s2.h , and s4.h in that order, but client_code_2.cpp includes s3.h, s2.h , and s4.h in that order. Then, B::g violates the One Definition Rule (ODR) because it has two inconsistent and incompatible implementations that can't both be rightone that tries to fall A::f(int) and one that tries to call A::f(double).

So don't write namespace using declarations or using directives in a header file, or before an #include directive in an implementation file. You are liable to affect the meaning of later code by causing namespace pollution, by taking an incomplete snapshot of the names that you want to import, or both. (Note the qualifier "namespace using declarations or using directives." This doesn't apply to writing class member using declarations to bring in base class member names as needed.)

In all headers, and in all implementation files before the last #include, always explicitly namespace-qualify all names. In implementation files after all #includes, you can and should write namespace using declarations and directives liberally. This is the right way to reconcile code brevity with modularity.

    Team LiB
    Previous Section Next Section