User Tools

Site Tools


c_namespaces

Overview

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:

Questions

  • “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?

Terminology

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

Also, namespace identifier is “… either a previously unused identifier, in which case this is original-namespace-definition or the name of a namespace, in which case this is extension-namespace-definition.”

Extensibility (namespaces are "open")

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.

using (declarations and definitions)

Distinction

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.”

using-Declarations

Read this carefully.

“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)
}

Useful for introducing base class members into derived class definitions.

using-Directives

cppreference.com link here.

“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.

Tradeoffs

  • 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.

Composition and selection

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
  ...
}

Nested namespaces

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 aliases

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.

Inline namespaces (new in C++11)

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.”

Unnamed namespaces

Its members have potential scope from their point of declaration to the end of the translation unit, and have internal linkage. The aim is to preserve locality of code rather than to present an interface to users.

#include "header.h"
namespace {
  int a;
  void f()  {...}
  int g()   {...}
}

An unnamed namespace has an implied using-directive.

“Unnamed namespaces in different translation units are different. As desired, there is no way of naming a member of an unnamed namespace from another translation unit.”

Argument dependent lookup (ADL)

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.txt · Last modified: 2018/02/01 14:50 by rpjday