Document number J16/01-0009 = WG21 N1295 Partial specialization of function templates ============================================ Peter Dimov, pdimov@mmltd.net with advice from David Abrahams, abrahams@mediaone.net Overview -------- C++ currently has * class template explicit specializations; * class template partial specializations; * function template explicit specializations. This leads many users to believe that function templates can be partially specialized; some of them even come up with the logical extension to the syntax on their own and are quite surprised that their compiler complains. Indeed, the standard in 14/2 explicitly forbids the "natural" function template partial specialization syntax. Why introduce them ------------------ [This section is written with the assumption that the reader will understand the proposed function template partial specialization syntax by analogy to class template partial specialization. For a detailed description, please refer to the following two sections] In many situations, a partial specialization of a function template can be emulated with an overload: template void f(T); template void f(T*); // specialize 'f' for pointers template void f(T*); // overload 'f' for pointers This has lead many to think that partial specializations are not necessary. However, there are cases where overloads do not suffice: * It is not possible to add an overload to a closed scope, like a class (or namespace std - but see LWG issue 226 and the paper "User supplied specializations of standard library algorithms".) A partial specialization may be used to specialize a function template that is a member of a closed scope. * When a template parameter does not appear in the function argument list, it is not possible to overload on it: template void f(); // 'f' cannot be overloaded template void f(); // but it can be specialized * Existing code may depend on the fact that a function template is not overloaded: template void f(T); template void user1(It first, It last) { typedef typename std::iterator_traits::value_type Vt; std::for_each(first, last, &f); // must cast &f to void (*) (Vt) if overloaded // std::for_each is used as an example only; in this trivial case // an explicit loop gets around the problem easily } struct base {}; struct derived: base {}; void user2() { derived d; f(d); // must use f(static_cast(d)) if overloaded } * Existing code grants friendship to a template function The overload will not inherit the access, but a specialization will. In summary, partial specializations are virtually indistinguishable from the primary template from the compiler's point of view. This makes them ideal for modifying the behavior of an existing template function without breaking or otherwise negatively affecting code that already depends on it. An often-used approach is to emulate function template partial specialization with helper classes: template struct f_helper { static void f(); }; template inline void f() { f_helper::f(); } template struct f_helper // specialize f() for pointers { static void f(); // specialized behavior } This approach works (although the code has a distinct "workaround" look). Unfortunately, libraries rarely provide helper classes for the users (or other library authors) to specialize. Even the standard C++ library, which has been designed with extensibility in mind, does not have helper classes. How could they work ------------------- This section is provided as a proof-of-concept. It is understood that the core working group may want to produce a new proposal from the ground up. Informally (see Appendix A for proposed changes to the standard), partial specialization of function templates would work as follows: template void f(); // primary template template<> void f(); // explicit specialization template void f(); // partial specialization void g() { f(); // calls the primary template f(); // calls the explicit specialization f(); // calls the partial specialization } Selecting the specialization to instantiate can be determined using the class template partial specialization rules. The situation becomes a bit more involved when multiple overloaded templates are present: template void f(T); // #1 template void f(T*); // #2 template<> void f(int); // #1.1 template<> void f(int*); // #2.1 template<> void f(int); // equivalent to #1.1 template<> void f(int*); // equivalent to #2.1 because of 14.5.5.2/1 template void f(U*); // #1.2 template void f(U**); // #2.2 In this case the primary template that is being specialized can be determined by the following procedure: For each primary template candidate, substitute the template arguments from the specialization into the primary template signature; if the two signatures match exactly, this primary template is being specialized. Example: #1.2 -> #1: the substitution is T = U*. #1 becomes void f(U*) and matches #1.2 exactly. #1.2 -> #2: T = U*, #2 becomes void f(U**), no match. This process always selects one primary template, except in the corner cases where: * multiple cv-qualifiers fold into one: template void f(T const *); template void f(T *); template void f(U const *); // ambiguous, U const const == U const * multiple references collapse into one: template void f(T &); template void f(T); template void f(U &); // ambiguous, U & & == U & Appendix A: Proposed changes ---------------------------- 7.3.3/9: - change the note to refer to partial specializations in general: "Note: template partial specializations are found by looking up the primary template and then considering all partial specializations of that template. If a using-declaration names a template, partial specializations introduced after the using-declaration are effectively visible because the primary template is visible (14.5.4)." 14/2: - remove the second sentence - change the note to read: "Note: if the declarator-id is a template-id, the declaration declares a template partial specialization (14.5.4)." 14/4: - change "class template partial specialization" to "template partial specialization" 14.5.4: - change section name to "Template partial specializations" 14.5.4/1: - remove all occurrences of the word "class". 14.5.4/4: - optionally provide an example for a function template partial specialization: template T1 f(T2 (&t2) [I]); template T f(T* (&t) [I]); template T1* f(T2 (&) [I]); template int f(T* (&t) [5]); template T1 f(T2* (&a) [I]); 14.5.4/5: - remove the word "class" in the second sentence 14.5.4/6: - not sure about that one 14.5.4/7: - remove the word "class" in the third sentence 14.5.4/9: - remove the word "class" in the first sentence 14.5.4/11 (new paragraph): A function template partial specialization specializes a primary template if and only if, after substituting the template arguments provided in the specialization template argument list into the primary template declaration, the resulting function signature matches that of the specialization. [Note: each function template partial specialization specializes at most one primary template.] 14.5.4/12 (new paragraph): [Example: template void f(T x); // primary template #1 template void f(U* y); // primary template #2 template void f(V* z); // specialization of #1, T = V* template void f(W** w); // specialization of #2, U = W* -- end example.] 14.5.4.1/1: - remove the first occurrence of "class" in the first sentence - change the second "class" to "template" in the first sentence - remove the word "class" in the second sentence - remove the word "class" in "the use of the class template is ambiguous" 14.5.4.2: - change section name to "Partial ordering of template specializations" 14.5.4.2/1: (change to): For two template partial specializations (that specialize the same primary template,) the first is at least as specialized as the second if, given the following rewrite to two function templates, the first function template is at least as specialized as the second according to the ordering rules for function templates (14.5.5.2): - synthesize a unique class template with the same parameter list as the primary template; - the first function template has the same template parameters as the first partial specialization and has a single function parameter whose type is a class template specialization of the synthesized class template with the template arguments of the first partial specialization; - the second function template has the same template parameters as the second partial specialization and has a single function parameter whose type is a class template specialization of the synthesized class template with the template arguments of the second partial specialization. 14.5.4.2/2 (change example to): template class X { }; template class X { }; // #1 template class X { }; // #2 template class __unique; template void __f(__unique); // #A template void __f(__unique); // #B 14.5.4.2/3 (new paragraph): [Example: template U f (V); template U f (V); // #1 template T f (T); // #2 template class __unique; template void __f(__unique); // #A template void __f(__unique); // #B -- end example.]