Namespace Association ("inline namespace")

Jason Merrill
2008-02-27
Document Number N2535=08-0045
Revision 8

Introduction

This paper provides proposed wording for the extension referred to in N2013 as "namespace association". The syntax in this revision of the paper has changed significantly from previous revisions.

The Problem

In N1344, Herb Sutter discussed the problems of shared library versioning specifically with regard to allowing the C++98 and C++0x standard libraries to coexist, going over various options and the problems with them.

On page 7, he writes "One might think that a more C++-ish way of hoisting a facility into two namespaces would be to use a using-declaration. One would be almost right."

It does seem that using would be a good way to separate the actual implementation from the namespace that we want other code to interact with. But as Herb notes, the problem with this is that we can only specialize a template in its actual namespace, not a namespace it has been imported into. Also, argument-dependent lookup breaks down if library components are split across multiple namespaces.

The Proposed Solution

The proposed extension allows the use of the inline keyword in a namespace-definition to specify that members of the namespace can be defined and specialized as though they actually belong to the enclosing namespace. This is referred to as namespace association because of its effects on argument-dependent lookup.

This extension has been implemented in the GNU and EDG compilers for several years now (expressed with a special using-directive, rather than a change to the namespace definition itself), and is getting a fair amount of use.

The syntax in this proposal has changed based on suggestions from other members of the committee, notably Daveed Vandevoorde.

For instance:

namespace Lib
{
  inline namespace Lib_1
  {
    template <typename T> class A; 
  }

  template <typename T> void g(T);
}
...
struct MyClass { ... };
namespace Lib
{
  template<> class A<MyClass> { ... };
}

int main()
{
  Lib::A<MyClass> a;
  g(a);  // ok, Lib is an associated namespace of A
}

Proposed Wording

In [namespace.def]

Change the grammar as follows:
original-namespace-definition:
	inlineopt namespace identifier { namespace-body }
extension-namespace-definition:
	inlineopt namespace original-namespace-name { namespace-body }
unnamed-namespace-definition:
	inlineopt namespace { namespace-body }
Also add new paragraphs:

If the optional initial inline keyword appears in a namespace-definition for a particular namespace, that namespace is declared to be an inline namespace. The inline keyword may be used on an extension-namespace-definition only if it was previously used on the original-namespace-definition for that namespace.

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are considered to be associated namespaces ([basic.lookup.argdep]) of one another, and a using directive ([namespace.udir]) which names the inline namespace is implicitly inserted into the enclosing namespace. Furthermore, each member of the inline namespace can subsequently be explicitly instantiated ([temp.explicit]) or explicitly specialized ([temp.expl.spec]) as though it were a member of the enclosing namespace. Finally, looking up a name in the enclosing namespace via explicit qualification ([namespace.qual]) will include members of the inline namespace brought in by the using directive even if there are declarations of that name in the enclosing namespace.

These properties are transitive: if a namespace N contains an inline namespace M, which in turn contains an inline namespace O, then the members of O can be used as though they were members of M or N. The set of namespaces consisting of the innermost non-inline namespace enclosing an inline namespace O, together with any intervening inline namespaces, is the enclosing namespace set of O.

In [basic.lookup.argdep]

Add after the bulleted list in paragraph 2:

If an associated namespace is an inline namespace ([namespace.def]), its enclosing namespace is also included in the set. If an associated namespace contains inline namespaces, those inline namespaces are also included in the set. In addition, if the argument is the name or address of a set of overloaded functions and/or function templates...

In [temp.explicit]

In paragraph 2:

An explicit instantiation shall appear in an enclosing namespace of its template. If the name declared in the explicit instantiation is an unqualified name, the explicit instantiation shall appear in the namespace where its template is declared or, if that namespace is inline ([namespace.def]), any namespace from its enclosing namespace set.

In [temp.expl.spec]

-2- An explicit specialization shall be declared in the nearest enclosing namespace of the template namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member, or, if that namespace is inline ([namespace.def]), any namespace from its enclosing namespace set. An explicit specialization of a member template, member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later in the namespace in which the explicit specialization was declared, or in a namespace that encloses the one in which the explicit specialization was declared.

In [dcl.meaning] paragraph 1

When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or of an inline namespace within that scope [namespace.def]), and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id.

In [namespace.qual] paragraph 2

Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), let S be the set of all declarations of m in X and in the transitive closure of all namespaces nominated by using-directives in X and its used namespaces, except that using-directives that nominate non-inline namespaces ([namespace.def]) are ignored in any namespace, including X, directly containing one or more declarations of m. No namespace is searched more than once in the lookup of a name. If S is the empty set, the program is ill-formed. Otherwise, if S has exactly one member, or if the context of the reference is a using-declaration (7.3.3), S is the required set of declarations of m. Otherwise if the use of m is not one that allows a unique declaration to be chosen from S, the program is ill-formed.

In [reserved.names]

Insert a new paragraph 2:

A program shall not declare namespace std to be an inline namespace ([namespace.def]).

References

Benjamin Kosnik, Versioning with Namespaces, N2013

Herb Sutter, Namespaces and Library Versioning, N1344