Chapter 9: Namespace & Linkage

Using Namespaces

Basic Usage

namespace NamespaceIdentifier
{
    // content of namespace here
}

NamespaceIdentifier::function(); // entering namespace

Example

// add.h
#ifndef ADD_H
#define ADD_H

namespace BasicMath { int add(int x, int y); }

#endif

// add.cpp
#include "add.h"
namespace BasicMath
{
    int add(int x, int y) // define the function add() inside namespace BasicMath
    {
        return x + y;
    }
}

// main.cpp
#include "add.h" // for BasicMath::add()

int main()
{
    std::cout << BasicMath::add(4, 3) << '\n';
}

Nested Namespaces

namespace Foo
{
    namespace Goo // Goo is a namespace inside the Foo namespace
    {
        int add(int x, int y) { return x + y; }
    }
}

namespace Foo::Goo // Goo is a namespace inside the Foo namespace (C++17 style)
{
    int add(int x, int y) { return x + y; }
}

Using Aliases

namespace Active = Foo::Goo; // active now refers to Foo::Goo
std::cout << Active::add(1, 2) << '\n'; // This is really Foo::Goo::add()

Local and Global Variables

int x { 2 }; // local variable, no linkage
{
    int x { 3 }; // this declaration of x refers to a different object than the previous x
}

Global Variables

Internal Linkage

static int g_x{}; // non-constant globals have external linkage by default, but can be given internal linkage via the static keyword
const int g_y{ 1 }; // const globals have internal linkage by default
constexpr int g_z{ 2 }; // constexpr globals have internal linkage by default

External Linkage

int g_x { 2 }; // non-constant globals are external by default
extern constexpr int g_z { 3 }; // useless, needed forward declaration in another file

// different file
extern int g_x; // variable forward declaration

Constants and Namespaces

Example 1

// constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants // define your own namespace to hold constants
{
    constexpr double pi { 3.14159 }; // constants have internal linkage by default
}

#endif

Example 2

// constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants
{
    extern constexpr double pi { 3.14159 };
    extern constexpr double avogadro { 6.0221413e23 };
}

#endif

Example 3 (Preferred)

// constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants
{
    inline constexpr double pi { 3.14159 }; // note: now inline constexpr
    inline constexpr double avogadro { 6.0221413e23 };
}

#endif

Static Local Variables

void incrementAndPrint()
{
    static int s_value{ 1 }; // This initializer is only executed once.
    ++s_value;
    std::cout << s_value << '\n';
} // s_value is not destroyed here, but becomes inaccessible because it goes out of scope

int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
    // Output: 2 3 4
}

Characteristics

Summary of Variable Types

Type Example Scope Duration Linkage Notes
int x; Block Automatic None
Static local variable static int s_x; Block Static None
Dynamic local variable int* x { new int{} }; Block Dynamic None
Function parameter void foo(int x) Block Automatic None
Internal non-const global variable static int g_x; Global Static Internal Initialized or uninitialized
External non-const global variable int g_x; Global Static External Initialized or uninitialized
Inline non-const global variable (C++17) inline int g_x; Global Static External Initialized or uninitialized
Internal constant global variable constexpr int g_x { 1 }; Global Static Internal Must be initialized
External constant global variable extern const int g_x { 1 }; Global Static External Must be initialized
Inline constant global variable (C++17) inline constexpr int g_x { 1 }; Global Static External Must be initialized

Storage Class Specifiers

Qualified Names

Example

using std::cout; // this using declaration tells the compiler that cout should resolve to std::cout

Unnamed (Anonymous) Namespaces

#include <iostream>

namespace // unnamed namespace
{
    void doSomething() // can only be accessed in this file
    {
        std::cout << "v1\n";
    }
}

int main()
{
    doSomething(); // we can call doSomething() without a namespace prefix
}

Inline Namespaces

Much like an unnamed namespace, anything declared inside an inline namespace is considered part of the parent namespace. However, unlike unnamed namespaces, inline namespaces don’t affect linkage.

inline namespace V1 // declare an inline namespace named V1
{
    void doSomething()
    {
         std::cout << "V1\n";
    }
}

Best Practices

  1. Using-directives allow unqualified access to all of the names from a namespace. Avoid using-directives altogether (except using namespace std::literals).
  2. Using-declarations are okay to use in .cpp files, after the #includes.
  3. Refer to explicit namespace qualifiers over using-statements.
  4. Avoid global variables whenever possible. Use local or const global variables if needed.
  5. Initialize your static local variables. Static local variables are only initialized the first time the code is executed, not on subsequent calls.