CS253: Software Development with C++

Fall 2021

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.

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

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:

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

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

What about conflicts? Consider two libraries:

Mathematics functionsMorality 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.