Show Lecture.Namespaces as a slide show.
CS253 Namespaces
Overview
- Namespaces, much like classes, contain symbols:
- functions (
std::exit()
)
- types
- classes (
std::runtime_error
)
- typedefs (
std::size_t
)
- variables
- scalars (
?
)
- objects (
std::cout
)
- constants (
std::nothrow
)
- templates (
std::vector<>
)
- namespaces (
std::regex_constants
)
Namespaces are useful for organization.
I put my stuff in my namespace;
you put your stuff in your namespace.
Popular Namespaces
- The most popular namespace,
std
, pronounced “standard”,
contains stuff defined by the C++ standard.
- Another is the global namespace, which has a zero-length name.
- Boost uses the
boost
namespace.
- You can create your own.
Namespace qualifiers
Namespace qualifiers look like class qualifiers:
#include <iostream>
int main() {
std::cout << "alpha" << std::endl;
}
alpha
Use a ::
prefix to access the global namespace:
int alpha = 42;
int main() {
int beta = 17;
std::cout << beta + ::alpha << '\n';
}
59
beta
is not in the global namespace.
It’s not in any namespace. It’s a local variable.
General using
To embrace an entire namespace:
#include <iostream>
using namespace std;
int main() {
cout << "gamma" << endl;
}
gamma
- Some people believe that
using namespace std;
is evil, and
litter their code with std::
prefixes.
- These people are need professional help.
Specific using
To import just one symbol:
#include <iostream>
using std::cout;
int main() {
cout << "delta" << endl;
}
c.cc:4: error: 'endl' was not declared in this scope
cout
worked, but endl
didn’t.
The global namespace
- The global namespace has a zero-length name, referred to by
::
.
- It’s reserved for your stuff.
- It’s considered poor form for a library or header unnecessarily pollute it,
because that’s not your stuff.
- Certain things must go there, for instance,
main()
.
C Compatibility
This is a valid C program.
Compatibility demands that it be a valid C++ program, as well.
#include <stdio.h>
#include <math.h>
int main() {
printf("π: %.19Lf\n", atanl(1)*4);
return 0;
}
π: 3.1415926535897932385
But, what about namespaces‽
C Compatibility
<math.h>
puts symbols such as atanl
into the global namespace,
since that’s where C programs expect them.
<cmath>
puts the same symbols into the std
namespace.
The file cmath
might look like this:
// This is cmath
namespace std {
#include <math.h>
}
This avoids duplication of information. It’s DRY!
Similar names
Don’t confuse these:
<string.h>
puts strlen
, strcpy
, … into the global namespace.
<cstring>
puts strlen
, strcpy
, … into the std
namespace.
<string>
puts the C++ string
class into the std
namespace.
Classes are closed
Once you’ve defined a class, you can’t add to it—it’s closed. Consider:
class Foo {
public:
int a,b,c;
};
class Foo {
public:
int d;
};
int main() { }
c.cc:6: error: redefinition of 'class Foo'
Namespaces are open
On the other hand, you can add to a namespace—it’s open. Consider:
namespace Math {
constexpr double PI = 3.14159;
}
namespace Math {
constexpr double E = 2.71828;
}
int main() {
cout << Math::PI * Math::E << '\n';
}
8.53972
std
is closed
However, the C++ standard forbids you adding anything to the
namespace std
, so it’s effectively closed, anyway.
Generally, compilers won’t stop you, because they have to allow
the implementation to put things into std
, and it’s difficult
to differentiate between you and the implementation, but it’s still
against the rules, so don’t do it.
namespace std {
void evil() { cout << "🙈🙉🙊\n"; }
}
int main() {
evil();
}
🙈🙉🙊
Nested namespaces
Namespaces can be nested.
namespace Pika {
constexpr int outer = 123;
// Attention, users: stay away from Private!
namespace Private {
constexpr int inner = 456;
}
}
int main() {
cout << Pika::outer << '\n'
<< Pika::Private::inner << '\n';
}
123
456
The anonymous namespace
If you don’t give your namespace a name, that’s an anonymous namespace.
The compiler makes up a name and does an implicit using
declaration:
namespace {
int foo;
}
It’s pretty much the same as:
namespace UNIQUE_NAME_42258107985915 {
int foo;
}
using namespace UNIQUE_NAME_42258107985915;
It’s intended to replace top-level static
declarations.
Namespace aliases
Long namespace names are good. For example, you wouldn’t want
a namespace called HP
—that’s a popular abbreviation:
- Hewlett-Packard
- Harry Potter
- Hit Points
- Hensel Phelps
Library implementors can define the namespace name to be
Hewlett_Packard
, and the user can create an easy-to-type alias:
namespace HP = Hewlett_Packard;
cost = HP::use_ludicrously_expensive_ink();
Symbol Resolution & Namespace Ambiguity
What about conflicts? Consider two libraries:
Mathematics functions | Morality functions |
cos( radians) | karma( action) |
tan( radians) | fasting() |
sin( radians) | sin( type) |
Uh, oh! Each library has a sin()
function!
Ambiguity
What will this do?
namespace Math { double sin(double); }
namespace Morality { double sin(double); }
using namespace Math;
using namespace Morality;
int main() {
sin(1.234);
}
c.cc:6: error: call of overloaded 'sin(double)' is ambiguous
It just plain fails. An unqualified sin
is ambiguous, because
there are two versions available. There is no priority order.
Also, the global namespace has no priority advantage, either.
Solutions
You can explicitly disambiguate:
using namespace Math;
using namespace Morality;
a = Morality::sin(b);
Or, you can only bring in the functions that you use:
using Math::tan;
using Morality::sin;
a = sin(b);
Ambiguity example
#include <iostream>
using namespace std;
int dec = 10;
int main() {
cout << dec << "°F\n";
return 0;
}
c.cc:5: error: reference to 'dec' is ambiguous
Ambiguity solutions
#include <iostream>
using namespace std;
int dec = 10;
int main() {
cout << ::dec << "°F\n"; // :: means global namespace
return 0;
}
10°F
#include <iostream>
using std::cout; // selective using declaration
int dec = 10;
int main() {
cout << dec << "°F\n";
return 0;
}
10°F
Or you could not use global variables.
Similar problem for functions, though.