Specializations and namespaces

Overview

We propose to allow specializing templates from within a different namespace.
The
motivation is that when we declare a new class, it is natural to want to provide associated
template specializations. For example, it is really painful that whenever I declare a class,
I need to class all open namespaces and enter namespace std just to specialize
std::hash as shown below
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
}
}
namespace std {
template<>
struct hash<A::B::C> {
size_t operator()(A::B::C const &c) { /* ... */ }
};
}
namespace A { /* Reenter namespace I am using */
namespace B {
/* ... */
}
}

Instead, I should be able to specialize std::hash<C> contiguous with the
rest of the definition of class C without having to break out of
its namespace:
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<>
struct ::std::hash<C> {
std::size_t operator()(C const &c) { /* ... */ }
};
/* ... */
}
}The technical point is that the primary template identifies
the template's namespace, so we don't need to use the namespace enclosing the specialization's definition
to identify it's namespace.

In in n3403, it is
anticipated that the (not yet existent) std::prompt will often be explicitly specialized.

Of course, this doesn't just apply for template classes in std. The same considerations
apply to specializing boost::fusion::traits::tag_of in the
Enabling Tag Dispatching example
in the Boost.Fusion documentation.

Specializing test classes in a putative test framework could also benefit
from something like this.

As one other example that some (but not all) may find interesting, I have worked on a multi-million line code base that
had a DEFINE_CLASS preprocessor macro to define classes according the the local coding standards. One of the things this macro
did was to specialize an XXX::Guid struct for this class, so that each conforming class had a GUID. In part due to the need
to exit and reenter the namespace in this macro, the entire codebase was placed in a single namespace (Yes, all of the namespace
info could be put in the macro, but avoiding such boilerplate is a primary motivation for the macro in the first place)

and so forth

Specializing in a different namespace

The rules for specializing in a different namespace are fairly straightforward.

friend specializations

Just as we allow external functions to be defined as friends (§11.3p6), we
allow specializations to be defined as friends. There is one important difference,
which is that the specialization is naturally placed in the namespace of the primary template,
not in namespace scope (as for function definitions in §11.3p6).
namespace A { // Repeated from Overview for easy reference
namespace B {
/* ... */
class C {
/* ... */
template<>
friend struct hash<C> { // Specializes std::hash
std::size_t operator()(C const &c) { /* ... */ }
};
};
}
}

Note that this only allows specializations to be defined as friends. Support for declaring
classes or primary templates is not required. (However, speaking personally, allowing declarations
of friend classes seems worthwhile in conjunction with
ADL for template parameters, but this is not required for this proposal...)

This has the benefit of being more general than just allowing specialization in another namespace.
For example, if we want to create a header that can be #includeed from within while within a namespace
(e.g., to use multiple versions of the same API within a single program),
we can write
// Foo.h
namespace :: { // Make sure we are in global scope
#include <string>
}
/* ... */

While this is nice, I am not recommending adding this functionality for the following reasons:

It does not handle the primary motivating example of specializing
as well as the approaches above. In particular, this approach doesn't
encapsulate the definitions of the specializations with the definition of
the class nearly as well.

By operating in the lexical scope of the namespace containing the primary template,
it is awkward to define the specialization as illustrated by the need to repeatedly
use A::B::C in the first example in this section.