Namespaces and Standard Includes
I was going to post this in response to a thread below where confusion and magic incantations are running rampant about namespaces and the standard header files. These are important issues that get glossed over a lot around here. It ran long, and I figure this is a broad enough subject for it's own post. So first I'm going to give a little primer on namespaces so people will hopefuly have a clearer understanding of what they're doing when they say
Namespaces
- Namespaces logically group a set of symbols with a name. In C you might name a function that's part of the display subsystem of your program display_text() or something. In C++, you might put a function called text() in the namespace display:
- You can bring symbols from some namespace into the current scope (global scope, another namespace, function scope, block scope) with using declarations. There are two forms of using declarations that apply to namespaces (and one that applies to class inheritence, but that's another matter):
Note that it does not bring it into the same namespace. The symbols will actually be as if they are in the closest namespace related to both scopes. This means that if you say:
the function text() will be as if it was declared in global scope. This means that if you had a function called text() in something, it would be called instead of display::text(). You can also use the following form:
This form does not do the same thing as the above. The function will be as if it was first declared at the point where the using declaration is in the current scope. That is, if you have a function called text() declared in the current namespace before the using declaration, it will be overridden by the one in the using declaration.
- You can explicitly scope symbols from a namespace by writing one or more namespaces with :: between them. This works exactly like a filesystem, except it's a little more flexible. The lookup procedure will go up as many namespaces it needs to to find a matching declaration. So for example:
First searches the current namespace (looking for something::display::text()), and when it doesn't find it there, it goes to the global scope and finds display::text(). This scales up to arbitrary complexity (though there is an upper limit on namespace depth in general). Further, if you prefix it with ::, you are explicitly stating that the symbol is rooted in the global scope. Ie. ::display::text() will work from anywhere.
- (now here's the neat part) Argument dependent lookup eliminates a lot of the need for explicitly scoping namespaces, and adds a lot of flexibility to how you design your class' interfaces. Essentially, when a function is called, and any of it's arguments include class types (ie display::monitor&), it will try to find the function in each possible namespace before doing the more normal lookup. For example:
Note the lack of qualification on the function calls. This is actually how operator overloading works in general. Without this, the standard C++ library headers would have to declare their operators (think operator<<) in global scope rather than in the std namespace. So as you can imagine, ADL applies equally to operator overload deduction.
And now how that relates to the standard libraries...
Standard Library Includes
There are three sets of include files in the Standard C++ Library. There is an additional set included by most compilers as a compatibility layer. They are:
- Those that make up the former STL: <vector>, <algorithm>, etc. (note that the STL is not the same as the standard library)
- Standard locale and iostream. These are templated versions of the pre-standard iostreams (see below). These are in <iostream> and company.
- C library headers. These are split into C compatibility (<stdio.h>) versions and C++ versions (<cstdio>).
- Many compilers also include a version of a pre-standard iostream library. These existed in the global namespace, did not use locale, and were not templates. These are in the files <iostream.h> and company. Currently, some compilers (gcc) just pull the standard versions into the global namespace, while others (vc) still supply the legacy iostream seperately. It's arguable which is correct, but the gcc way is more likely to break old programs in subtle ways.
Now here's the important bit. All of these headers should be declaring all their symbols in the std namespace, except the legacy versions. The only time any of the standard library headers should pull things into the global namespace is when a compatibility header like <stdio.h> is included. At that point, it should use direct using declarations for each of the symbols to pull them into the global namespace. None of these headers should be dumping the entire std namespace into the global namespace. What all this means is that if you include <stdio.h> and <cstring>,
Now, that isn't to say all compilers are correct as I described above. However, if you want your code to compile as well on compilers released a year from now as they do on current compilers, you should code as if it was correct. Visual C++ and GCC are both already pretty good about this stuff, but I don't know about Borland.
Keep this stuff in mind and you'll have a much easier time keeping up with compilers, and when you encounter upgrading difficulties, you'll know what to do.
I hope, as well, that there won't be so many statements to the effect of "just go using namespace std;. I don't know what it does, but it works" in here. Namespaces are a fundamental feature of C++, and it seems there's far too little understanding of how they work.
using namespace std;, then I'll explain how the standard library headers are supposed to interact with namespaces.Namespaces
- Namespaces logically group a set of symbols with a name. In C you might name a function that's part of the display subsystem of your program display_text() or something. In C++, you might put a function called text() in the namespace display:
namespace display
{
void text();
} // note lack of ;
- You can bring symbols from some namespace into the current scope (global scope, another namespace, function scope, block scope) with using declarations. There are two forms of using declarations that apply to namespaces (and one that applies to class inheritence, but that's another matter):
using namespace display; // brings all symbols from display into the nearest common namespaceNote that it does not bring it into the same namespace. The symbols will actually be as if they are in the closest namespace related to both scopes. This means that if you say:
namespace something
{
using namespace display;
}
the function text() will be as if it was declared in global scope. This means that if you had a function called text() in something, it would be called instead of display::text(). You can also use the following form:
using display::text; // brings display::text into the current scopeThis form does not do the same thing as the above. The function will be as if it was first declared at the point where the using declaration is in the current scope. That is, if you have a function called text() declared in the current namespace before the using declaration, it will be overridden by the one in the using declaration.
- You can explicitly scope symbols from a namespace by writing one or more namespaces with :: between them. This works exactly like a filesystem, except it's a little more flexible. The lookup procedure will go up as many namespaces it needs to to find a matching declaration. So for example:
namespace something
{
void func()
{
display::text();
}
}
First searches the current namespace (looking for something::display::text()), and when it doesn't find it there, it goes to the global scope and finds display::text(). This scales up to arbitrary complexity (though there is an upper limit on namespace depth in general). Further, if you prefix it with ::, you are explicitly stating that the symbol is rooted in the global scope. Ie. ::display::text() will work from anywhere.
- (now here's the neat part) Argument dependent lookup eliminates a lot of the need for explicitly scoping namespaces, and adds a lot of flexibility to how you design your class' interfaces. Essentially, when a function is called, and any of it's arguments include class types (ie display::monitor&), it will try to find the function in each possible namespace before doing the more normal lookup. For example:
namespace display
{
class monitor {}; // fill in the blanks
class printer {};
void text(monitor &disp, const std::string &str);
void text(printer &disp, const std::string &str);
}
int main()
{
display::monitor mon;
display::printer print;
text(mon, "hello, this'll show on the screen");
text(print, "and this on the printer.");
}
Note the lack of qualification on the function calls. This is actually how operator overloading works in general. Without this, the standard C++ library headers would have to declare their operators (think operator<<) in global scope rather than in the std namespace. So as you can imagine, ADL applies equally to operator overload deduction.
And now how that relates to the standard libraries...
Standard Library Includes
There are three sets of include files in the Standard C++ Library. There is an additional set included by most compilers as a compatibility layer. They are:
- Those that make up the former STL: <vector>, <algorithm>, etc. (note that the STL is not the same as the standard library)
- Standard locale and iostream. These are templated versions of the pre-standard iostreams (see below). These are in <iostream> and company.
- C library headers. These are split into C compatibility (<stdio.h>) versions and C++ versions (<cstdio>).
- Many compilers also include a version of a pre-standard iostream library. These existed in the global namespace, did not use locale, and were not templates. These are in the files <iostream.h> and company. Currently, some compilers (gcc) just pull the standard versions into the global namespace, while others (vc) still supply the legacy iostream seperately. It's arguable which is correct, but the gcc way is more likely to break old programs in subtle ways.
Now here's the important bit. All of these headers should be declaring all their symbols in the std namespace, except the legacy versions. The only time any of the standard library headers should pull things into the global namespace is when a compatibility header like <stdio.h> is included. At that point, it should use direct using declarations for each of the symbols to pull them into the global namespace. None of these headers should be dumping the entire std namespace into the global namespace. What all this means is that if you include <stdio.h> and <cstring>,
sprintf() should be accessible *both* in the global namespace *and* in std. sprintf("Hello %s", "world"); should work just as well as std::sprintf("Hello %s", "world");. On the other hand, if you at no point included <string.h>, you should *only* be able to get strlen as std::strlen unless you bring it in yourself. Further, C++ library classes should not be affected by this. Including <stdio.h> should not pull std::string into the global namespace.Now, that isn't to say all compilers are correct as I described above. However, if you want your code to compile as well on compilers released a year from now as they do on current compilers, you should code as if it was correct. Visual C++ and GCC are both already pretty good about this stuff, but I don't know about Borland.
Keep this stuff in mind and you'll have a much easier time keeping up with compilers, and when you encounter upgrading difficulties, you'll know what to do.
I hope, as well, that there won't be so many statements to the effect of "just go using namespace std;. I don't know what it does, but it works" in here. Namespaces are a fundamental feature of C++, and it seems there's far too little understanding of how they work.
