CS253: Software Development with C++

Fall 2022

Namespaces

Show Lecture.Namespaces as a slide show.

CS253 Namespaces

made at imgflip.com

Overview

Namespaces are useful for organization. I put my stuff in my namespace; you put your stuff in your namespace.

Popular Namespaces

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.

General using

To embrace an entire namespace:

#include <iostream>
using namespace std;
int main() {
    cout << "gamma" << endl;
}
gamma

Specific using

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’?

The global namespace

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

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:: or RGB:: 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.