N3867
Revision of N3730
2014-01-19
Mike Spertus
mike_spertus@symantec.com
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.
The primary motivation is that the natural place to specialize templates for a class is often alongside the definition of the class.
The rules for specializing in a different namespace are fairly straightforward.
For example, all of the following are OK. For clarity, the varying
sections are green.
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<>
struct ::std::hash<C> {
std::size_t operator()(C const &c) { /* ... */ }
};
/* ... */
}
}
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<>
struct std::hash<C> {
std::size_t operator()(C const &c) { /* ... */ }
};
/* ... */
}
}
using namespace std;
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<>
struct hash<C> {
std::size_t operator()(C const &c) { /* ... */ }
};
/* ... */
}
}
The same rule applies to declarations
using namespace std;
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<>
struct hash<C>;
/* ... */
}
}
I propose using the current lexical scope both because it seems more natural to me,
and is more compatible with the rules for defining friend members in §11.3p7.
This consistency is particularly nice when we discuss declaring friend specializations
below.
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<>
struct std::hash<C> { // OK
std::size_t operator()(C const &c) { /* ... */ }
};
struct less<A::B::C> { // Error: Not in namespace std. Also too awkward.
bool operator()(C const &c) { /* ... */ }
};
/* ... */
}
}
While the illustrations above have all been fully specializing classes, there is no
restriction on the type of specialization.
template<class C, int i = 0>
struct Foo {
std::string foo() { return "foo" };
};
template<class C>
struct Bar {
std::string bar() { return "bar" };
}
namespace A {
namespace B {
/* ... */
class C {
/* ... */
};
template<int i>
struct Foo<C, i> { // OK. Partial specialization
/* ... */
};
template<>
std::string Bar<C>::bar()) { // OK. ordinary method specialization
return "Special bar";
};
/* ... */
}
}
Change §14.7.3p2 [temp.expl.spec] as follows:
An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (7.3.1), any namespace from its enclosing namespace set. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later (7.3.1.2).Notes: Can it really be that simple? I will confirm the following items with the appropriate authorities before presenting to EWG.