Show Lecture.Namespaces as a slide show.
CS253 Namespaces
Overview
- Namespaces, much like classes, contain symbols.
They even share the
::
notation.
- functions (std::exit())
- types
- 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.
To embrace an entire namespace:
#include <iostream>
using namespace std;
int main() {
cout << "gamma" << endl;
}
gamma
- Some people believe that
using namespace std;
in a *.cc
file
is evil, and litter their code with std::
prefixes.
- These people are need professional help.
- Remember that
using namespace std;
in a header file is evil.
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 the global namespace, 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:
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
a pika
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
- Hewlett-Packard
- Harry Potter
- Hit Points
- Hensel Phelps
Long namespace names are good. For example, you wouldn’t want
to publish code defining a
a namespace HP
—that’s a popular abbreviation,
and likely to cause collisions.
A library implementor could 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>
#include <algorithm>
using namespace std;
int count = 10;
int main() {
cout << count << "\n";
return 0;
}
c.cc:6: error: reference to 'count' is ambiguous
Why did it fail?
There is a standard algorithm called std::count().
It counts things.
Ambiguity solutions
#include <iostream>
#include <algorithm>
using namespace std;
int count = 10;
int main() {
cout << ::count << "\n"; // :: means global namespace
}
10
#include <iostream>
#include <algorithm>
using std::cout; // selective using declaration
int count = 10;
int main() {
cout << count << "\n";
}
10
Or you could not use global variables.
Similar problem for functions, though.