"The road to hell is paved with global variables" -- Steve McConnell
#include <iostream> using namespace std; int foo = 1; // in global namespace int bar = 2; // in global namespace int Div2(int value) // in global namespace { return value / 2; } int main(void) { cout << foo << endl; // use global foo cout << bar << endl; // use global bar cout << Div2(8) << endl; // use global Div2 return 0; }
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 AdvancedCppProgramming { int foo = 1; int bar = 2; int Div2(int value) { return value / 2; } }
We need to qualify the symbols in the namespace:int main(void) { cout << foo << endl; //error, foo is undeclared cout << bar << endl; // error, bar is undeclared cout << Div2(8) << endl; // error, Div2 is undeclared return 0; }
The general form of a namespace definition is:int main(void) { cout << AdvancedCppProgramming::foo << endl; cout << AdvancedCppProgramming::bar << endl; cout << AdvancedCppProgramming::Div2(8) << endl; return 0; }
namespace user-defined-name { declaration/definition declaration/definition ... }
namespace AdvancedCppProgramming { int foo = 1; int bar = 2; } // Lots of other code here ... namespace AdvancedCppProgramming { int Div2(int value) { return value / 2; } }
namespace AdvancedCppProgramming { int foo = 1; int bar = 2; } int main(void) { cout << AdvancedCppProgramming::foo << endl; // Ok cout << AdvancedCppProgramming::bar << endl; // Ok cout << AdvancedCppProgramming::Div2(8) << endl; // error, Div2 is not part of namespace return 0; } namespace AdvancedCppProgramming { int Div2(int value) { return value / 2; } }
namespace AdvancedCppProgramming { int foo = 1; int bar = 2; int Div2(int value); // Declaration } int main(void) { cout << AdvancedCppProgramming::foo << endl; // Ok cout << AdvancedCppProgramming::bar << endl; // Ok cout << AdvancedCppProgramming::Div2(8) << endl; // Ok, compiles and links return 0; } namespace AdvancedCppProgramming { int Div2(int value) // Definition { return value / 2; } }
Helpers.h | Helpers.cpp | |
---|---|---|
namespace Helpers { extern int Counter; int FooFn(void); int BarFn(void); } |
namespace Helpers { int Counter = 25; int FooFn(void) { return 123; }; int BarFn(void) { return 456; } } |
#include <iostream> #include "Helpers.h" using namespace std; int main(void) { cout << FooFn() << endl; // error, FooFn undeclared cout << Helpers::FooFn() << endl; // Ok cout << Helpers::BarFn() << endl; // Ok cout << Helpers::Counter << endl; // Ok return 0; }
#include <iostream>
using namespace std;
int foo = 11; // in global namespace scope
int bar = 12; // in global namespace scope
namespace AdvancedCppProgramming
{
int foo = 1;
int bar = 2;
int f1(int x) { return x / 2; }
}
namespace AdvancedCppProgramming
{
int f2(void) { return foo * 33; } // AdvancedCppProgramming::foo
int f3(void) { return ::foo * 33; } // global foo
}
int f1(int x) // in global namespace scope
{
return x / 4;
}
int main(void)
{
int foo = 21; // local scope
int bar = 22; // local scope
cout << foo << endl; // from local scope
cout << AdvancedCppProgramming::foo << endl; // from ACP namespace scope
cout << ::foo << endl; // from global namespace scope
cout << AdvancedCppProgramming::f1(8) << endl; // from ACP namespace scope
cout << f1(8) << endl; // from global namespace scope
cout << ::f1(8) << endl; // from global namespace scope
return 0;
}
In the above, compilers from GNU (g++ 3.3), Microsoft (6.0 and 7.1), and Borland (5.5.1) compile and produce the expected results. However, using version 5.6.4 from Borland yields this compiler error:#include <iostream> #include <cmath> int main(void) { std::cout << sqrt(25.0) << std::endl; // calls sqrt in cmath std::cout << ::sqrt(25.0) << std::endl; // calls sqrt in cmath return 0; }
Adding a using namespace directive:Error E2268 main.cpp 19: Call to undefined function 'sqrt' in function main() Error E2268 main.cpp 20: Call to undefined function 'sqrt' in function main()
solves the "problem" with Borland's compiler. However, using the explicit namespace:#include <iostream> #include <cmath> using namespace std;
causes MS 6.0 to fail:std::cout << std::sqrt(25.0) << std::endl; // calls sqrt in cmath
main.cpp(22) : error C2039: 'sqrt' : is not a member of 'std'
Now what happens?
The new sqrt function hides the one from cmath and prevents us from using it now. Note that Microsoft 6.0, g++ 3.3, and Borland 5.6.4 handle this correctly, but MS 7.1 and Borland's 5.5 compiler issue an error. Borland says for both lines:#include <iostream> #include <cmath> double sqrt(double x) { return x; } int main(void) { std::cout << sqrt(25.0) << std::endl; // Which one? std::cout << ::sqrt(25.0) << std::endl; // Which one? return 0; }
And MS 7.1 says:Ambiguity between 'std::sqrt(double)' and 'sqrt(double)' in function main()
which can be fixed with:main.cpp(41) : error C2169: 'sqrt' : intrinsic function, cannot be defined
#pragma function(sqrt)
To "unhide" the function, we can "wrap" our new global:
The only compiler that had a "problem" with the above code was Borland's 5.6.4 compiler:#include <iostream> #include <cmath> namespace MyMathFunctions { double sqrt(double x) { return x; } } int main(void) { std::cout << MyMathFunctions::sqrt(25.0) << std::endl; // calls MyMathFunctions::sqrt in this file std::cout << sqrt(25.0) << std::endl; // calls sqrt in cmath std::cout << ::sqrt(25.0) << std::endl; // calls sqrt in cmath return 0; }
which, again, is what you would expect.Error E2268 main.cpp 73: Call to undefined function 'sqrt' in function main() Error E2268 main.cpp 74: Call to undefined function 'sqrt' in function main()
A more disturbing example:
The output should be (as produced by Borland's 5.6.4 compiler):#include <iostream> #include <cmath> double sqrt(double x) { return x; }; int main(void) { std::cout << ::sqrt(25.0) << std::endl; // Global sqrt function defined in this file std::cout << sqrt(25.0) << std::endl; // Global sqrt function defined in this file std::cout << std::sqrt(25.0) << std::endl; // sqrt from cmath return 0; }
However, MS 6.0 gives this error for the third line:25 25 5
and GNU 3.3 and MS 7.1 both compile without errors but print this:error C2039: 'sqrt' : is not a member of 'std'
25 25 25
A final attempt:
Results:#include <iostream> #include <cmath> using namespace std; double sqrt(double x) { return x; }; int main(void) { cout << sqrt(25.0) << endl; // sqrt function defined in this file cout << ::sqrt(25.0) << endl; // sqrt function defined in this file cout << std::sqrt(25.0) << endl; // sqrt function defined in cmath return 0; }
Borland 5.6.4 (1): Ambiguity between 'std::sqrt(double)' and 'sqrt(double)' in function main() Borland 5.5 (1,2): Ambiguity between 'std::sqrt(double)' and 'sqrt(double)' in function main() MS 6.0 (3): 'sqrt' : is not a member of 'std' GNU, MS 7.1: Compiles cleanly, calls sqrt in file for all.
The moral of the story is that you must be aware of the compliancy of your compiler, libraries, and the specifications of the C++ Standard.
namespace NS_1E266980_A661_48B6_94D1_C9DEA80A328B { // stuff } namespace NS_6FB60AE7_AEEE_4285_88A7_6F0C28B34B5B { // other stuff }
#include <iostream> namespace { double sqrt(double x) { return x; }; } int main(void) { std::cout << sqrt(25.0) << std::endl; // No qualification needed return 0; }
There isn't any sqrt() in the global namespace scope. With MSVC++ 6.0, this produces this error message:int main(void) { std::cout << ::sqrt(25.0) << std::endl; // Calling sqrt() function in global namespace scope return 0; }
error C2039: 'sqrt' : is not a member of '`global namespace''
If we have a symbol in an unnamed namespace that is the same as a global symbol in our program, we won't be able to access the symbol in the unnamed namespace. The example below is similar to the one above:
#include <iostream> #include <cmath> using namespace std; namespace { double sqrt(double x) { return x; }; } double sqrt(double x) { return x; }; int main(void) { cout << ::sqrt(25.0) << endl; // Global sqrt function defined in this file cout << sqrt(25.0) << endl; // Ambiguous (from global or unnamed namespace?) cout << std::sqrt(25.0) << endl; // OK, from std namespace return 0; }
Class Design Tip: When hiding symbols at the file scope, prefer to use unnamed namespaces over the already-overloaded-too-much C static keyword. Refresh your knowledge of the use of static.
#include <iostream> using namespace std; 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;} } } int main(void) { cout << DigiPenInstituteOfTechnology::Div2(8) << endl; cout << DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2(8) << endl; cout << DigiPenInstituteOfTechnology::AdvancedProgramming::Div2(8) << endl; return 0; }
using them requires a lot of typing:namespace AdvancedProgramming { int foo = 11; int bar = 12; int f1(int x) { return x / 2; } } namespace DigiPenInstituteOfTechnology { namespace IntroductoryProgramming { int foo = 21; int bar = 22; int Div2(int x) {return x / 2; } } namespace AdvancedProgramming { int foo = 31; int bar = 32; int Div2(int x) {return x >> 1; } } }
To allow unique namespaces and to shorten the names, you can create a namespace aliasint main(void) { cout << AdvancedProgramming::foo << endl; cout << DigiPenInstituteOfTechnology::AdvancedProgramming::foo << endl; cout << DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2(8) << endl; cout << DigiPenInstituteOfTechnology::AdvancedProgramming::Div2(8) << endl; return 0; }
Creating aliases for nested namespaces as well:// Declare these after the namespace definitions above namespace AP = AdvancedProgramming; namespace DIT = DigiPenInstituteOfTechnology; int main(void) { // Now, use the shorter aliases cout << AP::foo << endl; cout << DIT::AdvancedProgramming::foo << endl; cout << DIT::IntroductoryProgramming::Div2(8) << endl; cout << DIT::AdvancedProgramming::Div2(8) << endl; return 0; }
Note that you can't do this:namespace DIT_IP = DigiPenInstituteOfTechnology::IntroductoryProgramming; namespace DIT_AP = DIT::AdvancedProgramming; // uses previous alias // multiple aliases namespace CS120 = DIT::IntroductoryProgramming; namespace CS220 = DIT::AdvancedProgramming; int main(void) { // AdvancedProgramming::foo cout << AP::foo << endl; // DigiPenInstituteOfTechnology::AdvancedProgramming::foo; cout << DIT_AP::foo << endl; // DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2; cout << DIT_IP::Div2(8) << endl; cout << CS120::Div2(8) << endl; // DigiPenInstituteOfTechnology::AdvancedProgramming::Div2; cout << DIT_AP::Div2(8) << endl; cout << CS220::Div2(8) << 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;
Class Design Tip: Don't create very terse namespaces (like std). Create unique and meaningful namespaces and allow the user to create shorthand notation with aliases.
Namespace declarations are just like any other declaration in that they:using DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2; using DigiPenInstituteOfTechnology::IntroductoryProgramming::foo; int main(void) { // DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2 cout << Div2(8) << endl; // DigiPenInstituteOfTechnology::IntroductoryProgramming::foo cout << foo << endl; // Must qualify these cout << DigiPenInstituteOfTechnology::AdvancedProgramming::Div2(8) << endl; cout << DigiPenInstituteOfTechnology::AdvancedProgramming::foo << endl; return 0; }
using DigiPenInstituteOfTechnology::IntroductoryProgramming::Div2; using DigiPenInstituteOfTechnology::AdvancedProgramming::Div2; // Redeclaration of Div2
Example:
namespace Stuff
{
int foo = 11;
int bar = 12;
int baz = 13;
int f1(int x) { return x / 2; }
}
int foo = 21;
int bar = 22;
int f1(int x)
{
return x * 2;
}
int main(void)
{
using Stuff::foo; // similar to int foo = 11;
int foo = 3; // error, redefinition
cout << foo << endl; // Stuff::foo
cout << bar << endl; // ::bar
using Stuff::bar; // hides global bar
cout << bar << endl; // Stuff::bar
cout << ::bar << endl; // global bar
cout << f1(10) << endl; // global f1()
cout << Stuff::f1(10) << endl; // Stuff::f1()
using Stuff::f1;
cout << f1(10) << endl; // Now, Stuff::f1()
cout << ::f1(10) << endl; // global f1()
// error, baz is undeclared
int x = baz;
// should be either
int x = Stuff::baz;
// or
using Stuff::baz;
int x = baz;
return 0;
}
using namespace Stuff; int main(void) { cout << foo << endl; // Stuff::foo cout << bar << endl; // Stuff::bar cout << baz << endl; // Stuff::baz return 0; }
namespace Stuff
{
int foo = 11;
int bar = 12;
int baz = 13;
}
void f1(void)
{
int foo = 3; // local, hides nothing
int x = Stuff::foo; // OK
int y = bar; // error, bar is undeclared
}
int foo = 20;
int main(void)
{
using namespace Stuff;
cout << foo << endl; // error, foo is ambiguous
cout << ::foo << endl; // no problem, global
cout << Stuff::foo << endl; // no problem, Stuff::foo
cout << bar << endl; // Stuff::bar
cout << baz << endl; // Stuff::baz
int foo = 3; // OK, hides Stuff::foo
int x = Stuff::foo; // OK, use qualified name
return 0;
}
An example of employing using declarations and using directives
#include <iostream>
using namespace std;
namespace More {int foo = 10;}
namespace Stuff {int foo = 11;}
namespace Now {int foo = 12;}
int foo = 13;
int main(void)
{
using namespace More; // OK
using namespace Stuff; // OK, no redeclaration
cout << foo << endl; // Error is here: now foo is ambiguous
cout << More::foo << endl; // OK
cout << Stuff::foo << endl; // OK
using More::foo; // OK
cout << foo << endl; // OK, (More::foo) as if foo was declared in this scope
using namespace Now; // OK
cout << foo << endl; // OK, More::foo is still in scope
cout << Now::foo << endl; // OK, explicitly qualified
int foo = 3; // Error, redefinition (collides with More::foo)
int x = ::foo; // OK, global foo
return 0;
}
Suppose we remove this line above:
and move the local declaration of foo to the top of the function:using More::foo; // OK
How does this affect things?int main(void) { int foo = 3; // Local declaration using namespace More; // OK using namespace Stuff; // OK, no redeclaration . . . }
Summary:
Design Technique Using directives were designed for backward-compatibility with existing C++ code (which doesn't understand namespaces) to help support older code. Newer code should instead use the using declarations, to prevent name collisions and ambiguity. Using declarations, especially paired with namespace aliases, give the programmer better control over the global namespace.
A better way to write the above:#include <iostream> // For cout and endl using namespace std; // For access to *all* names inside std namespace int main(void) { cout << "Hello" << endl; return 0; }
Yet another way to write code that uses the C++ standard library:#include <iostream> // For cout and endl; using std::cout; // using declaration using std::endl; // using declaration int main(void) { cout << "Hello" << endl; // std::cout and std::endl return 0; }
Now we can write code like this (to ensure job security):#include <iostream> // For cout and endl int main(void) { std::cout << "Hello" << std::endl; return 0; }
#include <iostream> // For cout and endl; int main(void) { int cout = 16; // cout is an int cout << 1; // no effect std::cout << cout << 3 << std::endl; // std::cout is a stream (163) std::cout << (cout << 3) << std::endl; // std::cout is a stream (128) return 0; }