This is an old revision of the document!


Mechanism for expressing logical grouping – various properties of C++ namespaces, including:

  • terminology (declarations, definitions)
  • inline namespaces
  • unnamed namespaces
  • nested namespaces
  • namespace aliases
  • extensibility
  • namespaces and overloading
  • argument dependent lookup (ADL)

Links:

  • “A using-declaration adds a name to a local scope. A using-directive does not; it simply renders names accessible in the scope in which they were declared.” ????? What does this mean? (See below.)
  • Namespace member clashes only an issue when you try to use them?
namespace N {                  // [named] namespace definition
  bool f();                    // declaration of f()
  bool f(int);                 // declaration of f(int)
  bool g() { return true; }    // declaration/definition of g()
};

void N::f() { return false; }  // definition

using namespace N;             // using directive
using N::f;                    // using declaration

namespace {                    // unnamed namespace definition
  ...
};

namespace ATT = American_Telephone_and_Telegraph; // namespace alias

You can extend a namespace by adding names to it from multiple namespace declarations (possibly scattered across multiple header files) – useful for converting older programs to use namespaces:

namespace A {
  int f();
}

namespace A {
  int g();
}
  • You can extend a namespace using additional definitions based on the same name (to catch accidental misspellings).
  • You cannot declare a new member of a namespace outside a namespace definition using the qualifier syntax.
  • Extending the std namespace is undefined.
  • A namespace alias cannot be used to re-open a namespace.

NOTE: “A using-declaration adds a name to a local scope (also, “introduces a synonym into a scope”). A using-directive does not; it simply renders names accessible in the scope in which they were declared.”

This means that a using declaration will override a using definition; see “Composition and selection.”

“makes the symbol name from the namespace ns_name accessible for unqualified lookup as if declared in the same class scope, block scope, or namespace as where this using-declaration appears.”

using N::f;

NOTE: When used for an overloaded name, a using-declaration applies to all the overloaded versions:

namespace N {
  void f(int);
  void f(string);
};

void g() {
  using N::f;
  f(789);       // N::f(int)
  f("Bruce");   // N::f(string)
}

“From the point of view of unqualified name lookup of any name after a using-directive and until the end of the scope in which it appears, every name from ns_name is visible as if it were declared in the nearest enclosing namespace which contains both the using-directive and ns_name.”

using namespace N;

Keep their use inside scopes (functions) to avoid massive side effects. Even worse, don't include them in global scopes in header files.

  • If some qualification is really common for several names, use a using-directive for that namespace.
  • If some qualification is common for a particular name from a namespace, use a using-de- claration for that name.
  • If a qualification for a name is uncommon, use explicit qualification to make it clear from where the name comes.
  • Don’t use explicit qualification for names in the same namespace as the user.
namespace His_lib {
  class String { ... };
  template<typename T>
    class Vector { ... };
  // ...

namespace Her_lib {
  template<typename T>
    class Vector { ... };
  class String { ../ };
  // ...
}

// Does the order of the following matter?

namespace My_lib {
  using namespace His_lib;   // everything from His_lib
  using namespace Her_lib;   // everything from Her_lib

  using His_lib::String;     // resolve potential clash in favor of His_lib
  using Her_lib::Vector;     // resolve potential clash in favor of Her_lib
}

IMPORTANT: When looking into a namespace, names explicitly declared there (including names declared by using-declarations) take priority over names made accessible in another scope by a using-directive (see also §14.4.1). Consequently, a user of My_lib will see the name clashes for String and Vector resolved in favor of His_lib::String and Her_lib::Vector.

Can also rename for clarity:

namespace Lib2 {
  using namespace His_lib; // everything from His_lib
  using namespace Her_lib; // everything from Her_lib

  using His_lib::String; // resolve potential clash in favor of His_lib
  using Her_lib::Vector; // resolve potential clash in favor of Her_lib

  using Her_string = Her_lib::String; // rename
  template<typename T>
    using His_vec = His_lib::Vector<T>; // rename
  ...
}
void h();

namespace X {
  void g();
  // ...
  namespace Y {
    void f();
    void ff();
    // ...
  }
}

void X::Y::ff()
{
  f(); g(); h();   // all fine
}

void X::g()
{
  f();             // error: no f() in X
  Y::f();          // OK
}

void h()
{
  f();             // error: no global f()
  Y::f();          // error: no global Y
  X::f();          // error: no f() in X
  X::Y::f();       // OK
}

NOTE: C++17 allows namespace A::B::C { … }.

namespace American_Telephone_and_Telegraph { // too long
  // ...
}

American_Telephone_and_Telegraph::String s3 = "Grieg";
American_Telephone_and_Telegraph::String s4 = "Nielsen";

// use namespace alias to shorten names

namespace ATT = American_Telephone_and_Telegraph;

ATT::String s3 = "Grieg";
ATT::String s4 = "Nielsen";
  • Aliases can be used to refer to nested namespaces.
  • A namespace alias cannot be used to re-open a namespace.

Declarations inside an inline namespace will be visible in its enclosing namespace.

Supports versioning:

namespace Lib
  inline namespace V2_0 {
    ... V2.0 declarations ...
  }
  
  namespace V1_0 {
    ... V1.0 declarations ...
  }
}

Or create your own namespace based on that feature:

// file V99.h:
inline namespace V99 {
	void f(int);	// does something better than the V98 version
	void f(double);	// new feature
	// ...
}

// file V98.h:
namespace V98 {
	void f(int);	// does something
	// ...
}

// file Mine.h:
namespace Mine {
#include "V99.h"
#include "V98.h"
}

We here have a namespace Mine with both the latest release (V99) and the previous one (V98). If you want to be specific, you can:

#include "Mine.h"
using namespace Mine;
// ...
V98::f(1);	// old version
V99::f(1);	// new version
f(1);		// default version (in this case, V99)

One last weird variation, where you can choose your own default:

namespace MyDefault {
  inline
  #include "V3_0.h"      // the new default
  #include "V3_2.h"
  #include "V2_4_2.h"
}

“I do not recommend such intricate use of header files unless it is really necessary. The example above repeatedly violates the rules against including into a nonlocal scope and against having a syntactic construct span file boundaries (the use of inline); see §15.2.2. Sadly, I have seen worse.”

Its members have potential scope from their point of declaration to the end of the translation unit, and have internal linkage.

Coming soon … see here. Particularly useful with:

  • operator operands
  • template arguments

Basically:

  • If an argument is a class member, the associated namespaces are the class itself (including its base classes) and the class’s enclosing namespaces.
  • If an argument is a member of a namespace, the associated namespaces are the enclosing namespaces.
  • If an argument is a built-in type, there are no associated namespaces.
  • c_namespaces.1517483036.txt.gz
  • Last modified: 2018/02/01 11:03
  • by rpjday