Show Lecture.Namespaces as a slide show.
CS253 Namespaces
Overview
- Namespaces, much like classes, contain symbols.
They even share the
::
notation.
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
In general, it’s namespace-name::
thing-in-that-namespace.
The thing in the namespace could be a type (std::string),
an object (std::cout), a function (std::exit()), etc.
Use a ::
prefix to access the global namespace:
int alpha = 42;
int main() {
int beta = 17;
std::cout << beta + ::alpha << '\n';
}
59
As a local variable, beta
is not in the global namespace.
It’s not in any namespace.
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.
- Do not be angry with these people. Pity them. They’re probably
in that political party. You know which one. 🤫
- Remember that
using namespace std;
in a header file (*.h
)
is evil.
To import some symbols, but not all:
#include <iostream>
using std::cerr, std::clog;
int main() {
cerr << "delta";
clog << "epsilon";
cout << "zeta"; // 🦡
}
c.cc:7: error: ‘cout’ was not declared in this scope; did you mean
‘std::cout’?
- cerr and clog worked, but cout didn’t.
- Specifying several symbols after using is a C++17 feature.
The global namespace
- The global namespace has a zero-length name, referred to by
::
.
- It’s right there, just before the first colon.
- It’s reserved for your stuff.
- It’s poor form for a library or header to unnecessarily pollute the
global namespace, because that’s not your stuff.
- Note the “unnecessarily”. If the header
Zork.h
is documented
to put the class Zork
in the global namespace, then that’s
not pollution.
- main() must go in the global namespace.
That’s fine because main() is your stuff.
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:
General rule: if the filename ends with .h
, it puts symbols into
the global namespace. Otherwise, it puts symbols 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() {
std::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
Hewlett-Packard | |
Harry Potter | |
Hit Points | |
Hensel Phelps | |
Hindustan Petroleum | |
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
Consider two header files, which define the same color words
differently:
// RMA Resistor color code:
// https://en.wikipedia.org/wiki/Electronic_color_code
namespace RMA {
constexpr int black = 0, brown = 1, red = 2, orange = 3, yellow = 4,
green = 5, blue = 6, violet = 7, grey = 8, white = 9;
}
// RGB CSS color codes: see https://www.w3.org/TR/css-color-3/#svg-color
namespace RGB {
constexpr int black = 0x000000, brown = 0xa52a2a, red = 0xff0000,
orange = 0xffa500, yellow = 0xffff00, green = 0x008000,
blue = 0x0000ff, violet = 0xee82ee, gray = 0x808080, white = 0xffffff;
}
Lots of duplicates, but they’re in separate namespaces. Good!
Ambiguity
#include "resistor.h"
#include "rgb.h"
int main() {
cout << red << '\n'; // 🦡 want RMA red
}
c.cc:4: error: ‘red’ was not declared in this scope
Sure, we’ve got two definitions of red
, but neither is in
std::
or the global namespace. We had no using
declarations,
and no ::
namespace qualifiers on red
.
Ambiguity
Let’s use those namespaces:
#include "resistor.h"
#include "rgb.h"
using namespace RGB;
using namespace RMA;
int main() {
cout << red << '\n'; // 🦡 want RMA red
}
c.cc:6: error: reference to ‘red’ is ambiguous
It just plain fails. An unqualified red
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 disambiguate with an explicit RMA::
namespace qualifier:
#include "resistor.h"
#include "rgb.h"
using namespace RGB;
using namespace RMA;
int main() {
cout << RMA::red << '\n';
cout << hex << RGB::orange << '\n';
}
2
ffa500
Solutions
Or, you can only bring in the symbols that you use
via selective using declarations:
#include "resistor.h"
#include "rgb.h"
using RMA::red;
using RGB::orange;
int main() {
cout << red << '\n';
cout << hex << orange << '\n';
}
2
ffa500
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.