With structs wrapped up, we can move on to other features of C++. Today we’ll take a look at namespaces. We’ll cover the basics that C# provides, but go so much further and cover a lot of advanced functionality. Read on to learn all about them!

Table of Contents

Namespace Basics

Namespaces serve the same high-level purpose in C++ as they do in C#. They allow us to reuse identifiers and disambiguate them by namespace. The basic syntax even looks the same:

namespace Math
{
    struct Vector2
    {
        float X;
        float Y;
    };
}

Also like C#, we can reopen the namespace to add on to it by simply reusing the name:

namespace Math
{
    struct Vector2
    {
        float X;
        float Y;
    };
}
 
namespace Math
{
    struct Vector3
    {
        float X;
        float Y;
        float Z;
    };
}

We can also nest namespaces:

namespace Math
{
    namespace LinearAlgebra
    {
        struct Vector2
        {
            float X;
            float Y;
        };
    }
}

Accessing members of the namespace is a bit different. As we’ve seen with enums and structs, we continue to use the scope resolution operator (A::B) instead of C#’s dot syntax (A.B).

Math::Vector2 vec{2, 4}; // Refer to Vector2 in the Math namespace
DebugLog(vec.X, vec.Y); // 2, 4

To refer to the namespace implicitly created for the global scope, we use ::B instead of global::B as in C#:

int32_t highScore = 0;
 
class Player
{
    int32_t numPoints;
    int32_t highScore;
 
    void ScorePoints(int32_t num)
    {
        numPoints += num;
 
        // highScore refers to the data member
        if (numPoints > highScore)
        {
            highScore = numPoints;
        }
 
        // ::highScore refers to the global variable
        if (numPoints > ::highScore)
        {
            ::highScore = numPoints;
        }
    }
};

As of C++17, we can also use the scope resolution operator to create nested namespaces:

namespace Math::LinearAlgebra
{
    struct Vector2
    {
        float X;
        float Y;
    };
}

Unlike C#, we’re not limited to only putting types like structs and enums in a namespace. We can put anything we want there:

namespace Math
{
    // Variable
    const float PI = 3.14f;
 
    // Function
    bool IsNearlyZero(float val, float threshold=0.0001f)
    {
        return abs(val) < threshold;
    }
}

We can also put declarations inside the namespace and definitions outside:

namespace Math
{
    // Declarations
    struct Vector2;
    bool IsNearlyZero(float val, float threshold=0.0001f);
}
 
// Definitions
struct Math::Vector2
{
    float X;
    float Y;
};
bool Math::IsNearlyZero(float val, float threshold)
{
    return abs(val) < threshold;
}

The definitions need to be in either an enclosing namespace or at global scope:

namespace Math
{
    // Declarations
    struct Vector2;
    bool IsNearlyZero(float val, float threshold=0.0001f);
}
 
// Definitions
namespace Other
{
    // Compiler error: Other isn't an enclosing namespace or global scope
    struct Math::Vector2
    {
        float X;
        float Y;
    };
 
    // Compiler error: Other isn't an enclosing namespace or global scope
    bool Math::IsNearlyZero(float val, float threshold)
    {
        return abs(val) < threshold;
    }
}

Note that a namespace with only functions is another way to mimic a C# static class.

Using Directives

Explicitly writing out namespace names like Math:: gets tedious and verbose. As in C#, C++ has using directives to alleviate this issue. The syntax looks similar:

// Using directive
using namespace Math;
 
// No need for Math::
Vector2 vec{2, 4};
DebugLog(vec.X, vec.Y); // 2, 4

Unlike C# where using directives have to be at the top of a file, C++ allows them anywhere in the global scope, in a namespace, or even in any block:

namespace MathUtils
{
    // Using directive inside a namespace
    using namespace Math;
 
    bool IsNearlyZero(Vector2 vec, float threshold=0.0001f)
    {
        return abs(vec.X) < threshold && abs(vec.Y) < threshold;
    }
}
 
void Foo()
{
    // Using directive inside a function
    using namespace Math;
 
    // No need for Math::
    Vector2 vec{2, 4};
    DebugLog(vec.X, vec.Y); // 2, 4
}
 
enum struct Op
{
    IS_NEARLY_ZERO
};
 
bool DoOp(Math::Vector2 vec, Op op)
{
    if (op == Op::IS_NEARLY_ZERO)
    {
        // Using directive inside a block
        using namespace MathUtils;
 
        return IsNearlyZero(vec);
    }
    return false;
}

Also unlike C#, using directives are transitive. In the above, the MathUtils namespace has using namespace Math. That means any using namespace MathUtils implicitly includes a using namespace Math:

// Implicitly includes MathUtils' "using namespace Math"
using namespace MathUtils;
 
// No need for Math:: due to transitive using directive
Vector2 vec{2, 4};
 
// No need for MathUtils::
DebugLog(IsNearlyZero(vec)); // false

Even members added to the namespace after the using directive are included transitively:

namespace Math
{
    struct Vector2
    {
        float X;
        float Y;
    };
}
 
namespace MathUtils
{
    using namespace Math;
 
    bool IsNearlyZero(Vector2 vec, float threshold=0.0001f)
    {
        return abs(vec.X) < threshold && abs(vec.Y) < threshold;
    }
}
 
namespace Math
{
    struct Vector3
    {
        float X;
        float Y;
        float Z;
    };
}
 
void Foo()
{
    // Implicitly includes MathUtils' "using namespace Math"
    // Includes Vector3, even though it was after "using namespace Math"
    using namespace MathUtils;
 
    Vector3 vec{2, 4, 6};
    DebugLog(vec.X, vec.Y, vec.Z); // 2, 4, 6
}

Note that it is generally considered a bad practice to place using directives in the global scope of header files as it imposes the entirety of the namespace as well as any transitively-used namespaces on all files that #include it.

Inline Namespaces

C++ namespaces may be inline:

inline namespace Math
{
    struct Vector2
    {
        float X;
        float Y;
    };
}

This is similar to a non-inline namespace immediately followed by a using directive:

namespace Math
{
    struct Vector2
    {
        float X;
        float Y;
    };
}
using namespace Math;

We can therefore use the members of the namespace without the scope resolution operator or an explicit using directive:

Vector2 vec{2, 4};
DebugLog(vec.X, vec.Y); // 2, 4

As of C++20, we can add the keyword inline before each name except the first when defining nested namespaces:

// Math is a non-inline namespace
// LinearAlgebra is an inline namespace nested in Math
namespace Math::inline LinearAlgebra
{
    struct Vector2
    {
        float X;
        float Y;
    };
}
 
// Math:: still required as Math is not an inline namespace
Math::Vector2 vec{2, 4};
DebugLog(vec.X, vec.Y); // 2, 4

This adds convenience to a normal use case for inline namespaces. It’s typical to want to group together functionality, but also offer it piecemeal. For example, the C++ Standard Library offers user-defined literals for the string and hour types like this:

namespace std::inline literals::inline string_literals
{
    std::string operator""s(const char* chars, size_t len)
    {
        // ... implementation ...
    }
}
namespace std:inline literals::inline chrono_literals
{
    std::chrono::hours operator""h(long double val)
    {
        // ... implementation ...
    }
}
 
void UseAllLiterals()
{
    // Transitively use string_literals and chrono_literals
    // No need to specify each one
    using namespace std::literals;
 
    std::string greeting = "hello"s;
    std::chrono::hours halfHour = 0.5h;
}
 
void UseJustStringLiterals()
{
    // Use just string_literals
    using namespace std::literals::string_literals;
 
    std::string greeting = "hello"s;
    std::chrono::hours halfHour = 0.5h; // Compiler error
}
 
void UseJustChronoLiterals()
{
    // Use just chrono_literals
    using namespace std::literals::chrono_literals;
 
    std::string greeting = "hello"_s; // Compiler error
    std::chrono::hours halfHour = 0.5h;
}
Unnamed Namespaces

Namespaces may have no name. Just like with inline namespaces, these have an implicit using directive right after them:

namespace
{
    struct Vector2
    {
        float X;
        float Y;
    };
}
 
// Implicitly added by the compiler
// UNNAMED is just a placeholder for the name the compiler gives the namespace
using namespace UNNAMED;
 
// Can use members of the unnamed namespace
Vector2 vec{2, 4};
DebugLog(vec.X, vec.Y); // 2, 4

Because these namespaces have no name, there’s no way to explicitly refer to their members with the scope resolution operator (A::B) or name them in a using directive.

Specially, all members of unnamed namespaces, including nested namespaces, have internal linkage, just like static global variables.

// other.cpp
int32_t Global; // External linkage
namespace
{
    int32_t InNamespace; // Internal linkage
}
 
// test.cpp
extern int32_t Global; // OK: has external linkage
extern int32_t InNamespace; // Linker error: has internal linkage
                            // Also, no way to name the namespace here
void Foo()
{
    Global = 123;
    InNamespace = 456;
}
Using Declarations

Besides using directives, C++ also has using declarations:

namespace Math
{
    struct Vector2
    {
        float X;
        float Y;
    };
 
    struct Vector3
    {
        float X;
        float Y;
        float Z;
    };
}
 
// Use just Vector2, not Vector3
using Math::Vector2;
 
Vector2 vec2{2, 4}; // OK
Vector3 vec3a{2, 4, 6}; // Compiler error
Math::Vector3 vec3b{2, 4, 6}; // OK

Like variable declarations, we can name multiple namespace members in a single using declaration:

namespace Game
{
    class Player;
    class Enemy;
}
 
void Foo()
{
    // Use Vector2 and Player, not Vector3 or Enemy
    using Alias::Vector2, Game::Player;
 
    Vector2 vec2{2, 4}; // OK
    Vector3 vec3{2, 4}; // Compiler error
    Player* player; // OK
    Enemy* enemy; // Compiler error
}

Unlike using directives, using declarations actually add the specified namespace members to the block they’re declared in. This means some conflicts are possible:

namespace Stats
{
    int32_t score;
}
 
namespace Game
{
    struct Player
    {
        int32_t Score;
    };
}
 
bool HasHighScore(Game::Player* player)
{
    using Stats::score;
 
    int32_t score = player->Score; // Compiler error: score already declared
    return score > score; // Ambiguous reference to score
}

There’s one case where it’s OK to have more than one identifier with the same name: function overloads. With multiple using declarations referring to functions with the same name, we can create an overload set within the function:

namespace Game
{
    struct Player
    {
        int32_t Health;
    };
}
 
namespace Damage
{
    struct Weapon
    {
        int32_t Damage;
    };
 
    void Use(Weapon& weapon, Game::Player& player)
    {
        player.Health -= weapon.Damage;
    }
}
 
namespace Healing
{
    struct Potion
    {
        int32_t HealAmount;
    };
 
    void Use(Potion& potion, Game::Player& player)
    {
        player.Health += potion.HealAmount;
    }
}
 
void DamageThenHeal(
    Game::Player& player, Damage::Weapon& weapon, Healing::Potion& potion)
{
    using Damage::Use; // Now have one Use function
    using Healing::Use; // Now have two Use functions: an overload set
 
    Use(weapon, player); // Call the Use(Weapon&, Player&) overload
    Use(potion, player); // Call the Use(Potion&, Player&) overload
}
 
Game::Player player{100};
Damage::Weapon weapon{20};
Healing::Potion potion{10};
DamageThenHeal(player, weapon, potion);
DebugLog(player.Health); // 90
Namespace Aliases

The final namespace feature is a simple one: aliases. We can use these to shorten long, usually nested namespace names:

namespace Math
{
    namespace LinearAlgebra
    {
        struct Vector2
        {
            float X;
            float Y;
        };
    }
}
 
// mla is an alias for Math::LinearAlgebra
namespace mla = Math::LinearAlgebra;
 
mla::Vector2 vec2{2, 4};
DebugLog(vec2.X, vec2.Y); // 2, 4

This is pretty close to using N1 = N2 in C#. The major difference is that it can be placed in the global scope, in a namespace, or in any other block. It doesn’t need to be placed at the top of the file and only apply to that one file.

Conclusion

C++ namespaces are, in many ways, very similar to C# namespaces. As is typical, they form a rough superset of the C# functionality. Advanced features like inline namespaces, using declarations, and unnamed namespaces are available to us. We also have the ability to put variables and functions in them in addition to type definitions. We can even put using declarations and directives nearly anywhere, not just at the top of files.

As is typical with more power though, more complexity is involved. We need to avoid overly-broad using directives, pay attention to transitive using directives, and resolve identifier conflicts with using declarations.

Namespaces are used in nearly every C++ codebase, as they are in nearly every C# codebase. We now know the rules for how to use them, so we’re one step closer to effectively writing C++!