"The road to hell is paved with global variables" -- Steve McConnell
Namespaces
#include <iostream> int global_scope; // Visible from any file in the program. Only one // global symbol can have this name. (ODR) static int file_scope // Visible within this file only int main() { int local_scope; // Visible in main only if (some_condition) { int local_scope2; // Visible only within if } return 0; }
file1.cpp | file2.cpp | file3.cpp |
---|---|---|
int global_scope; static int file_scope; int fn1() { int local_scope; if (some_condition) { int local_scope2; } return 0; } |
|
|
#include <iostream> // cout, endl int foo = 1; // foo in global namespace int bar = 2; // bar in global namespace int Div2(int value) // Div2 in global namespace { return value / 2; } int main() // main in global namespace { std::cout << foo << std::endl; // use global foo std::cout << bar << std::endl; // use global bar std::cout << Div2(8) << std::endl; // use global Div2 return 0; }
instead of this:cout << foo << endl; // missing std:: namespace
It would have produced a compiler error:std::cout << foo << std::endl;
That's because cout and endl are in the std (standard) namespace, which is the subject of this discussion.test.cpp: In function 'int main()': test.cpp:13:3: error: 'cout' was not declared in this scope cout << foo << endl; // use global foo ^ test.cpp:13:3: note: suggested alternative: In file included from test.cpp:1:0: /usr/include/c++/5/iostream:61:18: note: 'std::cout' extern ostream cout; /// Linked to standard output ^ test.cpp:13:18: error: 'endl' was not declared in this scope cout << foo << endl; // use global foo ^ test.cpp:13:18: note: suggested alternative: In file included from /usr/include/c++/5/iostream:39:0, from test.cpp:1: /usr/include/c++/5/ostream:590:5: note: 'std::endl' endl(basic_ostream<_CharT, _Traits>& __os)
but this will limit their use to this file only. A better solution is to put them in a unique namespace:static int foo = 1; // file scope static int bar = 2; // file scope static int Div2(int value) // file scope { return value / 2; }
namespace IntroCppProgramming { int foo = 1; int bar = 2; int Div2(int value) { return value / 2; } }
We need to qualify the symbols in the namespace:int main() { std::cout << foo << std::endl; // error, foo is undefined std::cout << bar << std::endl; // error, bar is undefined std::cout << Div2(8) << std::endl; // error, Div2 is undefined return 0; }
The general form of a namespace definition is:int main() { std::cout << IntroCppProgramming::foo << std::endl; std::cout << IntroCppProgramming::bar << std::endl; std::cout << IntroCppProgramming::Div2(8) << std::endl; return 0; }
namespace user-defined-name { declaration/definition declaration/definition ... }
namespace IntroCppProgramming // Opening the namespace ... { int foo = 1; // Adding to the namespace ... int bar = 2; // Adding to the namespace ... } // Closing the namespace ... // Lots of other code here ... namespace IntroCppProgramming // Re-opening the namespace ... { int Div2(int value) // Adding to the namespace ... { return value / 2; } } // Closing the namespace ...
namespace IntroCppProgramming { int foo = 1; int bar = 2; } int main() { std::cout << IntroCppProgramming::foo << std::endl; // Ok std::cout << IntroCppProgramming::bar << std::endl; // Ok std::cout << IntroCppProgramming::Div2(8) << std::endl; // error, Div2 is not part of namespace (yet) return 0; } namespace IntroCppProgramming { // Now Div2 is part of the namespace int Div2(int value) { return value / 2; } }
namespace IntroCppProgramming { int foo = 1; int bar = 2; int Div2(int value); // Declaration/prototype } int main() { std::cout << IntroCppProgramming::foo << std::endl; // Ok std::cout << IntroCppProgramming::bar << std::endl; // Ok std::cout << IntroCppProgramming::Div2(8) << std::endl; // Ok, compiles and links return 0; } namespace IntroCppProgramming { int Div2(int value) // Definition { return value / 2; } }
Helpers.h (Declarations) | Helpers.cpp (Definitions) | |
---|---|---|
namespace Helpers { extern int Counter; int FooFn(); int BarFn(); } |
namespace Helpers { int Counter = 25; int FooFn() { return 123; } int BarFn() { return 456; } } |
You can use these from main.cpp like this:
Question: Why do we use the extern keyword in the header file?#include <iostream> // cout, endl #include "Helpers.h" int main() { std::cout << FooFn() << std::endl; // error, FooFn undeclared std::cout << Helpers::FooFn() << std::endl; // Ok std::cout << Helpers::BarFn() << std::endl; // Ok std::cout << Helpers::Counter << std::endl; // Ok return 0; }
Scope Resolution Operator
Example:#include <iostream> // cout, endl
int foo = 1; // global
int bar = 2; // global
void fn1()
{
int foo = 10; // local foo #1 hides global foo
int bar = foo; // local bar #1 hides global bar (set to local foo)
int baz = ::foo; // local baz #1 is set to global foo
if (bar == 10) // local bar #1
{
int foo = 100; // local foo #2 hides local #1 and global
bar = foo; // local bar #1 is set to local foo #2
foo = ::bar; // local foo #2 is set to global bar
}
::foo = foo; // global foo is set to local foo #1
::bar = ::foo; // global bar is set to global foo
std::cout << "foo is " << foo << std::endl; // local foo #1 is 10
std::cout << "bar is " << bar << std::endl; // local bar #1 is 100
std::cout << "::foo is " << ::foo << std::endl; // global foo is 10
std::cout << "::bar is " << ::bar << std::endl; // global bar is 10
}
Marked-up diagram
Notes:
Some compilers will even warn you when you do this, even though it's completely legal to do so.void foo(void) { int a; // Ok if (...) { int a; // Ok for (...) { int a; // Ok if (...) { int a; // Ok ... } ... } ... } }
Examples:The scope resolution operator tells the compiler exactly where to look for the symbol. This means that the compiler does not perform any kind of "searching" to look for the symbol. It will be found (if it exists) in the namespace specified. If it is not found, no searching will take place and the compiler will emit an error message.
However, if there is NO scope resolution operator, then the compiler will search through the various scopes looking for the symbol. It is this searching by the compiler that can lead to potential problems with ambiguity. If you always use the scope resolution operator for every access, you will never have any problems with your namespaces.
::foo() // No searching, calls global foo() Fred::foo() // No searching, calls Fred::foo() Barney::foo() // No searching, calls Barney::foo() Wilma::Betty::foo() // No searching, calls Wilma::Betty::foo() foo() // Which foo()? A search must be performed.
Nested Namespaces
Eventually, even a namespace is going to have hundreds or thousands of symbols.
#include <iostream> // cout, endl namespace DigiPenInstituteOfTechnology { int Div2(int x) {return x * 0.5;} namespace IntroductoryProgramming { int Div2(int x) {return x / 2;} } namespace AdvancedProgramming { int Div2(int x) {return x >> 1;} } } int main(void) { std::cout << DigiPenInstituteOfTechnology::Div2(8) << std::endl; std::cout << DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2(8) << std::endl; std::cout << DigiPenInstituteOfTechnology::AdvancedProgramming::Div2(8) << std::endl; return 0; }
Unnamed Namespaces
What happens when we run out of unique names for namespaces?namespace NS_1E266980_A661_48B6_94D1_C9DEA80A328B { // stuff } namespace NS_6FB60AE7_AEEE_4285_88A7_6F0C28B34B5B { // other stuff }
#include <iostream> // cout, endl namespace { double sqrt(double x) { return x; } } int main() { std::cout << sqrt(25.0) << std::endl; // No qualification needed return 0; }
Example: (Ignore the fact that the sqrt functions don't work)
These are the error messages from the GNU compiler:#include <iostream> // cout, endl #include <cmath> // sqrt namespace { double sqrt(double x) { return x; }; // Line 6: available only in this file } double sqrt(double x) { return x; }; // Line 9: global int main() { std::cout << ::sqrt(25.0) << std::endl; // Global sqrt function defined in this file std::cout << std::sqrt(25.0) << std::endl; // sqrt from std namespace std::cout << sqrt(25.0) << std::endl; // Line 15: Ambiguous (from global or unnamed namespace?) return 0; }
sqrt.cpp: In function 'int main()': sqrt.cpp:15: error: call of overloaded 'sqrt(double)' is ambiguous sqrt.cpp:9: note: candidates are: double sqrt(double) sqrt.cpp:6: note: double::sqrt(double)
Design Tip
When hiding symbols at the file scope, prefer to use unnamed namespaces over the already-overloaded-too-much C static keyword.
Namespace Aliases
Given these namespaces:using them requires a lot of typing:namespace AdvancedProgramming { int foo = 11; int bar = 12; int f1(int x) { return x / 2; } } namespace IntroductoryProgramming { int foo = 21; int bar = 22; int Div2(int x) {return x / 2; } }
To allow unique namespaces and to shorten the names, you can create a namespace aliasint main() { std::cout << AdvancedProgramming::foo << std::endl; std::cout << IntroductoryProgramming::Div2(8) << std::endl; return 0; }
Notes:// Declare these after the namespace definitions above namespace AP = AdvancedProgramming; namespace IP = IntroductoryProgramming; int main() { // Now, use the shorter aliases std::cout << AP::foo << std::endl; std::cout << IP::foo << std::endl; std::cout << AP::f1(8) << std::endl; std::cout << IP::Div2(8) << std::endl; return 0; } void fn1() { // You can "re-alias" a namespace (must be in different scope) namespace AP = IntroductoryProgramming; std::cout << AP::f1(8) << std::endl; // Now, an error no f1 in AP std::cout << AP::Div2(8) << std::endl; // Ok std::cout << IP::Div2(8) << std::endl; // Same as above }
Creating aliases for nested namespaces as well:
namespace AdvancedProgramming { int Div2(int x) {return x >> 1;} } namespace AP = AdvancedProgramming; namespace DigiPenInstituteOfTechnology { int Div2(int x) {return x / 2;} namespace IntroductoryProgramming { int Div2(int x) {return x / 2;} } namespace AdvancedProgramming { int Div2(int x) {return x >> 1;} } }
Note that you can't do this:namespace DIT = DigiPenInstituteOfTechnology; namespace DIT_IP = DigiPenInstituteOfTechnology::IntroductoryProgramming; namespace DIT_AP = DIT::AdvancedProgramming; // uses previous alias // multiple aliases namespace CS120 = DIT::IntroductoryProgramming; namespace CS225 = DIT::AdvancedProgramming; int main(void) { // These are all equivalent std::cout << DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2(8) << std::endl; std::cout << DIT::IntroductoryProgramming::Div2(8) << std::endl; std::cout << DIT_IP::Div2(8) << std::endl; std::cout << CS120::Div2(8) << std::endl; // These are equivalent std::cout << DIT_AP::Div2(8) << std::endl; std::cout << CS225::Div2(8) << std::endl; return 0; }
because AP is not a member of the DIT namespace. (It's not a substitution like the preprocessor performs with a #define.)// DigiPenInstituteOfTechnology::AdvancedProgramming::foo?? cout << DIT::AP::foo << endl;
Another example:
Now, with one alias like this:namespace DigiPenInstituteOfTechnology { namespace GAM400 { namespace Graphics { // stuff here } namespace Physics { // stuff here } namespace Network { // stuff here } } }
You can access symbols inside the hierarchy something like this (assume there is an initialize function in each):namespace DIT = DigiPenInstituteOfTechnology;
It may seem like a lot of typing, but it is very clear what is going on. It is not unusual to have lots of modules in your program that all have an initialize or init function. This is better than having several global functions with names like this:DIT::GAM400::Graphics::initialize(); DIT::GAM400::Physics::initialize(); DIT::GAM400::Network::initialize(); DIT::GAM350::Graphics::initialize(); DIT::PRJ450::Network::initialize();
Global symbols tend to have very long names in order to minimize the chance of name collisions. Local symbols can't conflict with other local symbols, so they have much shorter names.Gam400_Graphics_initialize(); Gam400_Physics_initialize(); Gam400_Network_initialize(); Gam350_Graphics_initialize(); PRJ450::Network::initialize();
Design Tip
Don't create very terse namespaces (like std). Create unique and meaningful namespaces and let the user create shorthand notation with aliases.
Using Declarations
A using declaration allows you to make specific symbols accessible without requiring the namespace and scope resolution operator.Example:
1. namespace Stuff
2. {
3. int foo = 11; // Stuff::foo
4. int bar = 12; // Stuff::bar
5. int baz = 13; // Stuff::baz
6. }
7.
8. using Stuff::foo; // Make foo globally accessible to all code below
9. // without namespace qualifier
10.
11. void f1()
12. {
13. Stuff::foo = 21; // OK, using namespace
14. foo = 22; // OK, namespace not required
15.
16. using Stuff::bar; // Make bar available to code below (in this scope/function only)
17. bar = 30; // OK
18. }
19.
20. void f2()
21. {
22. int foo = 3; // This is a new foo, it hides Stuff::foo
23. Stuff::foo = 4; // OK, qualified
24. ::foo = 5; // OK, global foo (i.e. Stuff::foo)
25. }
26.
27. int main()
28. {
29. foo = 23; // OK because of using declaration above
30.
31. using Stuff::baz; // Make baz available in this function only
32. baz = 40; // OK
33.
34. return 0;
35. }
Summary:
1. using Stuff::baz; // 'Stuff' has not been declared yet
2.
3. namespace Stuff
4. {
5. int foo = 11; // Stuff::foo
6. int bar = 12; // Stuff::bar
7. int baz = 13; // Stuff::baz
8. }
9.
10. namespace Stuff2
11. {
12. int bar = 111; // Stuff2::bar
13. }
14.
15. using Stuff::foo; // Make foo globally accessible to all code below
16. // without namespace qualifier
17.
18. void f1()
19. {
20. Stuff::foo = 21; // OK, using namespace
21. foo = 22; // OK, namespace not required
22. bar = 30; // Error, Stuff:: namespace required
23.
24. using Stuff::bar; // Make bar available to code below (in this scope/function only)
25. bar = 30; // OK
26.
27. int bar = 5; // Error: redeclaration. The using declaration above already declared it
28.
29. using Stuff2::bar; // Error: redeclaration. The using declaration above already declared it
30. }
31.
32. int foo = 222; // Error: redeclaration. The using declaration above already declared it
33.
34. int main()
35. {
36. foo = 23; // OK because of using declaration above
37. bar = 30; // Error, bar needs namespace
38.
39. using Stuff::baz; // Make baz available in this function only
40. baz = 40; // OK
41.
42. return 0;
43. }
Errors generated by the clang compiler:
Note: The errors are detected when the using declaration is encountered, not when the symbol is accessed:udecls2.cpp:1:7: error: use of undeclared identifier 'Stuff' using Stuff::baz; // 'Stuff' has not been declared yet ^ udecls2.cpp:22:3: error: use of undeclared identifier 'bar' bar = 30; // Error, Stuff:: namespace required ^ udecls2.cpp:27:7: error: declaration conflicts with target of using declaration already in scope int bar = 5; // Error: redeclaration. The using declaration above already declared it ^ udecls2.cpp:6:7: note: target of using declaration int bar = 12; // Stuff::bar ^ udecls2.cpp:24:16: note: using declaration using Stuff::bar; // Make bar available to code below (in this scope/function only) ^ udecls2.cpp:29:17: error: target of using declaration conflicts with declaration already in scope using Stuff2::bar; // Error: redeclaration. The using declaration above already declared it ^ udecls2.cpp:12:7: note: target of using declaration int bar = 111; // Stuff2::bar ^ udecls2.cpp:6:7: note: conflicting declaration int bar = 12; // Stuff::bar ^ udecls2.cpp:32:5: error: declaration conflicts with target of using declaration already in scope int foo = 222; // Error: redeclaration. The using declaration above already declared it ^ udecls2.cpp:5:7: note: target of using declaration int foo = 11; // Stuff::foo ^ udecls2.cpp:15:14: note: using declaration using Stuff::foo; // Make foo globally accessible to all code below ^ udecls2.cpp:37:3: error: use of undeclared identifier 'bar' bar = 30; // Error, bar needs namespace ^ 6 errors generated.
using Stuff::bar; // Make bar available in this scope. using Stuff2::bar; // Error is detected here: redeclaration. The using declaration above already declared it bar = 30; // Which bar? The compiler never reaches this to see the ambiguity.
Using Directives
A using directive allows you to make all of the names in a namespace visible at once:
We can make them all accessible with a using directive:namespace Stuff { int foo = 11; // Stuff::foo int bar = 12; // Stuff::bar int baz = 13; // Stuff::baz }
using namespace Stuff; // Everything in Stuff (foo, bar, baz) is visible from here down in the file int main() { std::cout << foo << std::endl; // Stuff::foo std::cout << bar << std::endl; // Stuff::bar std::cout << baz << std::endl; // Stuff::baz return 0; }
int main() { using namespace Stuff; // Everything in Stuff (foo, bar, baz) is visible only in main, now std::cout << foo << std::endl; // Stuff::foo std::cout << bar << std::endl; // Stuff::bar std::cout << baz << std::endl; // Stuff::baz return 0; } // Unqualified members in Stuff not available here.
namespace Stuff2 { int bar = 111; // Stuff2::bar } using namespace Stuff; // Make all symbols from Stuff (foo, bar, baz) accessible. using namespace Stuff2; // Make all symbols from Stuff2 (bar) accessible. foo = 10; // OK, Stuff::foo bar = 30; // Error is detected here. Which bar? It is ambiguous.
Stuff::bar = 100; // OK Stuff2::bar = 200; // OK
namespace Stuff
{
int foo = 11; // Stuff::foo
int bar = 12; // Stuff::bar
int baz = 13; // Stuff::baz
}
void f1()
{
int foo = 3; // local, hides nothing
int x = Stuff::foo; // OK
int y = bar; // error, bar is unknown
}
int foo = 20; // global ::foo
int main()
{
using namespace Stuff; // Stuff's members are now accessible without Stuff::
// qualifier within main
std::cout << ::foo << std::endl; // no problem, global
std::cout << Stuff::foo << std::endl; // no problem, Stuff::foo
std::cout << foo << std::endl; // error, foo is ambiguous (global ::foo or Stuff::foo?)
std::cout << bar << std::endl; // Stuff::bar
std::cout << baz << std::endl; // Stuff::baz
int foo = 3; // OK, hides Stuff::foo and global ::foo
int x = Stuff::foo; // OK, use qualified name
x = foo; // OK, local foo above
x = ::foo; // OK, global foo
return 0;
}
Summary:
Using directives were designed for backward-compatibility with existing C++ code (which doesn't understand namespaces) to help support older code. They should be used cautiously when writing new code, especially if they are used at a global scope.
The Standard Namespace (std)
Now that we've seen some of the details of how namespaces are created and used, we can see how they can be applied.
#include <iostream> // For cout and endl using namespace std; // For access to *all* names inside std namespace int main() { cout << "Hello" << endl; return 0; }
#include <iostream> // For cout and endl using std::cout; // using declaration, global scope using std::endl; // using declaration, global scope int main() { cout << "Hello" << endl; // std::cout and std::endl return 0; }
#include <iostream> // For cout and endl int main() { using std::cout; // using declaration, local scope using std::endl; // using declaration, local scope cout << "Hello" << endl; // std::cout and std::endl return 0; }
#include <iostream> // For cout and endl int main() { std::cout << "Hello" << std::endl; return 0; }
Operator precedence chart for C++#include <iostream> // For cout and endl; int main() { int cout = 16; // cout is an int cout << 1; // no effect std::cout << cout << 3 << std::endl; // std::cout is a stream, prints: 163 std::cout << (cout << 3) << std::endl; // std::cout is a stream, prints: 128 return 0; }
Error message:#include <algorithm> // STL functions using namespace std; // make the whole C++ universe available! int count = 0; int increment() { return ++count; // error, identifier count is ambiguous }
Hopefully, that should convince you that using directives should be used judiciously.count.cpp: In function 'int increment()': count.cpp:8:12: error: reference to 'count' is ambiguous return ++count; // error, identifier count is ambiguous ^~~~~ count.cpp:4:5: note: candidates are: int count int count = 0; ^~~~~ In file included from /usr/include/c++/7/algorithm:62:0, from count.cpp:1: /usr/include/c++/7/bits/stl_algo.h:4076:5: note: templatetypename std::iterator_traits<_Iterator>::difference_type std::count(_IIter, _IIter, const _Tp&) count(_InputIterator __first, _InputIterator __last, const _Tp& __value) ^~~~~
Self check You should understand why these examples are legal or illegal.
Understanding the Big Picture™