C++: Namespace

I moved the (growing) section on namespaces from the C++ Snippets page to this separate page, where I had already written some notes about using anonymous and inline namespaces in the same translation unit/file (see below).

The page therefore now also got a more generic and encompassing title.

General Information

Defining

A namespace definition begins with the keyword namespace followed by the namespace name:

namespace ns_name
{
    int var = 10;
    void foo () { std::cout << "abc\n"; }
    void bar ();                          // Is declared here, will be defined later.
}                                         // <- No semicolon (;) after the closing brace!

void ns_name::bar ()                      // Function definition (with fully qualified namespace)
{
    std::cout << "Hello, world!\n";
}

Accessing

Access to (nested) members is done with the scope resolution operator (::) and the name of the namespace (or its alias, see below):

ns_name::foo();
ns_name::nested_ns_name::bar();

Extending

A namespace can also be extended (e.g. adding functions, variables, classes, etc.), even in other files:

FileA.cpp:

namespace ns_name
{
    int var = 10;
}

FileB.cpp:

namespace ns_name
{
    void bar ();
}

Nesting

Namespaces can be nested and the resolution of namespace variables is hierarchical.

namespace ns_name
{
    void foo () { std::cout << "abc\n"; }
    
    namespace nested_ns_name
    {
        void bar () { std::cout << "xyz\n"; }
    }
}

Since C++17, the nesting can be represented much more compact, with the scope resolution operator (::):

namespace ns_name::nested_ns_name
{
    // ...
}

An ordinary nested namespace has unqualified access to its parent’s members, but the members of the parent namespace do not have unqualified access to the nested namespace (unless it is declared as inline, see below).


Alias

If a namespace name is very long (sometimes needed, to prevent naming collisions), one can abbreviate it with an alias, which then also can be used for accessing the namespace members:

namespace a_very_long_namespace_name
{
    void foo () { std::cout << "abc\n"; }
}

namespace nn = a_very_long_namespace_name;

nn::foo(); // Same as a_very_long_namespace_name::foo();

Using default namespaces

One can also avoid to prepend the namespace in front of symbols with the using namespace directive: That tells the compiler that the subsequent code is making use of names in the specified namespace (normal scope rules still apply).

Or, instead of doing it for the whole namespace, one could also use a using declaration (without namespace!): Declare only a particular item within a namespace for that use.

Attention: In code that will be included elsewhere (e.g. header files), one should always use the fully qualified namespace name to prevent surprises (for the user), what will be called!

namespace ns_name1
{
    void foo () { std::cout << "Hello, world (NS1: foo)!\n"; }
    void bar () { std::cout << "Hello, world (NS1: bar)!\n"; }
}

namespace ns_name2
{
    void foo () { std::cout << "Hello, world (NS2: foo)!\n"; }
    void bar () { std::cout << "Hello, world (NS2: bar)!\n"; }
}

using namespace ns_name1;      // Make the whole namespace available.
using           ns_name2::bar; // Make only this element from the namespace available.

// But: In this example, the combination of 'using' and 'using namespace' at the same time
//      (when both namespaces use the same function names) triggers the error below!

foo();
bar(); // Maybe an error: 'Ambiguous call to overloaded function', see above.

Global Namespace

If an identifier is not declared in an explicit namespace, then it is part of the implicit global namespace
(i.e. everything outside of any namespace is considered to belong to the global namespace).

In general, try to avoid making declarations at global scope when possible (except for the entry point main function, which is required to be in the global namespace).

To explicitly qualify a global identifier, use the scope resolution operator (::) at the beginning (without a namespace):

::SomeGlobalFunction();
::a_global_var = 1;

This differentiates the identifier from anything with the same name in any other namespace and also helps to make your code easier for others to understand.


Anonymous Namespace

An anonymous namespace is a namespace without a name – and you might ask: “for what case may that be useful?”; well:
It ensures that the entities in this namespace are limited to that particular translation unit (i.e. file), and thus has a similar effect as a static variable or (free) function and helps with encapsulation, hiding of implementation details, and preventing namespace pollution:

File helper.cpp:

namespace
{
    int var = 10;
}

void function ()
{
    std::cout << var; // Accessing the anonymous namespace variable.
}

File main.cpp:

int main()
{
    std::cout << var; // Error: 'var' is unknown/unreachable in this file.
    ...
}

Inline Namespace

An inline namespace allows the identifiers of the nested inline namespace to behave as if they were identifiers of the parent/enclosing namespace (this property is transitive!).

In contrast to an ordinary nested namespace, members of an inline namespace are treated as members of the parent namespace.

But: That can also lead to compile errors due to ambiguous symbols, see example below: “inline v2 a” and “inline v21 a” are considered to be on the same level from the compiler’s point of view!

Example use case: An inline namespace could be used for a versioning mechanism, to manage changes to the public interface of a library.

namespace ns_name
{
    namespace v1
    {
        std::string a = "v1";
    }
    
    inline namespace v2
    {
        // std::string a = "v2"; // Would be a compile error: Ambiguous symbol due to both being inlined!
        
        inline namespace v21
        {
            std::string a = "v2.1";
        }
    }
}

std::cout << ns_name::a          << '\n'; // Prints 'v2.1'
std::cout << ns_name::v2::a      << '\n'; // Prints 'v2.1'
std::cout << ns_name::v2::v21::a << '\n'; // Prints 'v2.1'
std::cout << ns_name::v1::a      << '\n'; // Prints 'v1'

The same behavior can also be achieved by using the using declarative inside namespaces:
A using-directive that names the inline namespace is implicitly inserted in the enclosing namespace (similar to the implicit using-directive for the unnamed namespace).

namespace ns_name
{
    namespace v1
    {
        std::string a = "v1";
    }
    
    namespace v2
    {
        std::string a = "v2";
        
        namespace v21
        {
            std::string a = "v2.1";
        }
        
        using namespace v21;
    }
    
    using namespace v2;
}

std::cout << ns_name::a          << '\n'; // Prints 'v2'
std::cout << ns_name::v2::v21::a << '\n'; // Prints 'v2.1'
std::cout << ns_name::v1::a      << '\n'; // Prints 'v1'

More on anonymous and inline namespaces

While looking up something about the concept of anonymous/unnamed namespaces in C++, I came across this tutorial page, and in its comment section, there was a question I also had in my mind after reading the article: What happens when some of those approaches clash in the same translation unit? 🤔

Unfortunately, it wasn’t shown in the examples or answered satisfactorily at that time in the comments, therefore I made a few quick tests for myself and jotted down the findings, probably for my own reference in the future… (and to spoil the result a bit: It all played out as I expected, no weird surprises).

First, a quick reminder on the terms and use-cases (more details can be found at the linked sites and elsewhere on the net):

Test #1: (Static) Free function + anonymous namespace

We have a (static) free function defined in the global namespace scope, and also an anonymous namespace with a function of the same name:

#include <iostream>

static // ... or not, doesn't matter in this example.
void print () { std::cout << "global namespace\n"; }

namespace
{
    void print () { std::cout << "anonymous namespace\n"; }
}

int main (int argc, char* argv[])
{
    print();
    return 0;
}

Result

As expected: The compiler doesn’t know, which variant to use: Compiler error C2668: “Ambiguous call to overloaded function”.

Test #2: Anonymous namespace + inline namespace

We have an anonymous namespace with a function defined inside of it, and another namespace, which is declared as inline and which contains a function with the same name and signature:

#include <iostream>

namespace
{
    void print () { std::cout << "anonymous namespace\n"; }
}

inline
namespace v1
{
    void print () { std::cout << "v1 namespace\n"; }
}

int main (int argc, char* argv[])
{
    print();
    return 0;
}

Result

The same, as expected: The compiler doesn’t know, which variant to use: Compiler error C2668: “Ambiguous call to overloaded function”.

Test #3: Multiple inline namespaces

Both namespaces, v1 and v2, are marked as inline at the same time in the same file:

#include <iostream>

inline
namespace v1
{
    void print () { std::cout << "v1 namespace\n"; }
}

inline
namespace v2
{
    void print () { std::cout << "v2 namespace\n"; }
}

int main (int argc, char* argv[])
{
    print();
    return 0;
}

Result

Again, the same, as expected: The compiler doesn’t know, which variant to use: Compiler error C2668: “Ambiguous call to overloaded function”.

Example: API versioning with an inline namespace

And at the end, here’s an example of a case where inline is used for versioning the API:

Function print() of api_v2 is used by default, since that namespace is marked as inline.
But one can still access the older version of api_v1 by specifying the exact namespace.

#include <iostream>

namespace api_v1
{
    void print () { std::cout << "api_v1 namespace\n"; }
}

inline          // On a lookup-match, this namespace's version will be used.
namespace api_v2
{
    void print () { std::cout << "api_v2 namespace\n"; }
}


int main (int argc, char* argv[])
{
    print();         // Using api_v2 by default, due to the 'inline' marker.
    api_v1::print(); // Explicitly requesting the old version.
    return 0;
}
api_v2 namespace
api_v1 namespace

And when the time comes for a api_v3, the inline specifier needs to move:

#include <iostream>

namespace api_v1
{
    void print () { std::cout << "api_v1 namespace\n"; }
}

namespace api_v2
{
    void print () { std::cout << "api_v2 namespace\n"; }
}

inline                                 // Moved to here.
namespace api_v3
{
    void print () { std::cout << "api_v3 namespace\n"; }
}


int main (int argc, char* argv[])
{
    print();         // Now api_v3 is the new default.
    api_v1::print();
    api_v2::print();
    return 0;
}
api_v3 namespace
api_v1 namespace
api_v2 namespace

External resources