Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Why I Hate Namespaces

Last updated Jan 1, 2003.

In their early days, namespaces seemed as the right solution at the right time for the name conflict problem. I admit that I greeted them with enthusiasm in the early 1990s. However, more then a decade later, I have second thoughts about this feature. Here’s why.

The Promise

According to the "defensive programming" idiom, designers must design their libraries so that naive users wouldn’t encounter bugs even the bugs result from incorrect usage. The C++ standards committee adhered to this idiom with respect to namespaces. Instead of declaring standard classes, functions and overloaded operators globally, they are all declared in the reserved namespace std:: (operator new and operator delete, in all their flavors, are an exception to the rule). The rationale behind this decision seemed plausible: this would avert name conflicts between standard C++ components and third-party libraries and legacy code that might be using the same names.

The Problem

Today only few programmers use fully-qualified names in their code systematically. This is no surprise since using fully-qualified names is tedious, clutters up the code, and in most case it’s redundant anyway. Instead, they resort to the denounce habit of adding a

using namespace std; 

directive at the beginning of their program, which effectively injects all the names in namespace std:: into the global namespace. In other words, we had to go through all the troubles of namespaces only to go back to square one!

You may argue that by using a set of using declarations instead of a wholesale using directive, one can enjoy both worlds -- avoiding name conflicts while refraining from injecting std:: names into the global namespace indiscriminately. This may be correct to some extent. However, at a certain point you will find this practice almost as tedious as using fully-qualified names. Consider a simple program that uses a few containers, algorithms and iterators. Every time you modify the program, say replacing std::vector with std::list or std::sort() with std::partial_sort(), you’ll have to locate the corresponding using declaration and change it accordingly. Furthermore, as C++0x is adding new nested namespaces under std::, this is going to prove quite confusing. For instance, in order to use shared_ptr, you have to know that it’s declared in the nested namespace std::tr1::, whereas the C++98 relative operators are declared under std::rel_ops::, and so on. I’m not saying that it’s impossible to teach programmers which namespace contains which declarations but at the end of the day, this becomes a trivia quiz without a justified goal.

Argument Dependent Lookup

My main concern has to do with is known as Argument Dependent Lookup (ADL). In essence, ADL is meant to transfer some of the burden of using fully-qualified names from the shoulders of the human programmer to the compiler. A concrete example will illustrate the point. Consider the following program:

#include <iostream>
int main()
{
 std::cout<<"hello world!"<<std::endl;
}

This program is written according to the orthodox conventions of using fully qualified names instead of using declarations or -- heaven forbid -- a using namespace std; directive. The problem is that this program is still incomplete, at least in theory. The cout expression uses the overloaded << operator twice. As you may have guessed, this operator is also declared in namespace std, so why does this program still compiles? ADL is the answer. When the compiler encounters the token << it performs a context sensitive search. It looks for it in the global namespace, but also in the namespace in which cout is declared, i.e., std::. Without the ADL, you’d have to rewrite the program like this:

#include <iostream>
int main()
{
std::cout.operator<<("hello world!").operator<<(std::endl);
}

In a more realistic example, deeply nested templates are used with various overloaded operators and manipulators from different namespaces, making such coding style hardly tolerable. This is why ADL was added to C++ in the first place. And this is exactly where our troubles begin. The ADL lookup algorithm is so complex that very few compilers, if any, get it right. As a result, seemingly valid code that compiles with compiler either refuses to compile with a different compiler, or it compiles but behaves differently because the different interpretations of the ADL cause different dependent names to be selected. Even committee members aren’t always unanimous with respect to the correct interpretation of the ADL in some cases. If committee members find it confusing, you certainly don’t expect intermediate level programmers to understand how the ADL works. Bottom line: You can’t live without the ADL, but with the ADL things get pretty messy and confusing.

The sad conclusion that namespaces were a mistake. At this stage, there’s not much that we can do about it. Namespaces metastasized into every nook and cranny of C++ (as well as other languages such as C# and XML, albeit with significant differences). However, a recent TR1 proposal hints at the desired future direction. While no one will admit it, the standards committee is silently reverting to naming conventions as the ultimate tool against name conflicts -- just as in the pre-namespace era. The following proposal to Add Hash Tables to the Standard Library acknowledges the futility of a namespace-based approach to the naming of TR1 hashed containers. The problem is that hashed containers, e.g., hased_map, hashed_set etc. have been available as a non-standard extension of the Standard Library of several leading vendors for several years now. Seemingly, the TR1 hashed containers could still use the same names by declaring them in a special namespaces, and thus avoid potential conflicts between the non-standard hashed_map and the standard hashed_map. This approach was rejected, though: "since using directives are common and naive users often gloss over the question of which namespace a class is in, namespace segregation would probably not suffice to avoid user confusion." Instead, The Library Working Group chose a different naming scheme for the new containers: unordered_set, unordered_map, unordered_multiset, and unordered_multimap. Recall that these containers are still guarded by a nested namespace, e.g., std::tr1::unordered_set. However, the LWG probably doesn’t trust this machinery very much, nor should it. In spite of the availability of namespaces, the safest strategy against name conflicts is still good old underscores and unique names, very much like in mid 1970s C. Interestingly, C seems to be doing very well without namespaces until this very day.