ISO: WG21/N0495 ANSI: 94-0108 Author: John Max Skaller Date: April 1994 Reply to: maxtal@suphys.physics.su.oz.au TEMPLATE SPECIALISATIONS and NAMESPACES --------------------------------------- Executive Summary: A specialisation of a template may be declared or defined in a namespace other than that of the original template. THE STATUS QUO -------------- At present it is my interpretation that a specialisation must be declared and defined in the namespace in which the template it specialises is declared and defined. THE HIJACKING PROBLEM --------------------- Specialisations in the same namespace have a serious software engineering problem. Suppose a programmer provides a library which defines a template X, and also uses an unspecialised X for some U in the imlementation of the library. template void f(T) { ..} template class X { .. }; void g() { X xu; f(xu); } If another programmer then defines a specialisation of X, two definitions of X exist. void f(U) { ..} // specialisation class X { .. }; // specialisation void h() { X xu; f(xu); } A diagnostic may or may not be issued, and the source code for the library may not be available to allow the programmer to determine what the cause of the problem is. This hijacking can only be prevented by the library writer documenting every use of all templates, implementation details that may change with versions of the library and which the application programmer should not be exposed to. // IN MY LIBRARY I USE THE UNSPECIALISED FUNCTION f(U) // AND THE UNSPECIALISED CLASS X // PLEASE DONT SPECIALISE THESE TEMPLATES A similar problem occurs if the library uses specialisations, but the requirement that these must be declared helps alleviate the problem: however this is still only documentation, no error is detected if the user simply provides a conflicting specialisation that matches the declaration. Simply saying that the behaviour in the presence of conflicting definitions is undefined is not adequate: it is lousy management to give a programmer responsibilities without the information or techniques necessary to act responsibly. HIJACKING THE STANDARD LIBRARY ------------------------------ The problem for user specialisations of Standard Library templates is worse: the only way to specialise them is to invade the Standard Library namespace. // user code .. namespace std { class basic_string { .. } } It would be better to reserve the "std" namespace exclusively for implementors, but preventing user specialisations of Standard Library templates is draconian. In addition it is unclear that a declaration added to a namespace is actually visible if the extension is done after a using declaration has been issued: at present my interpretation is that only declarations visible at the point of issuing the using declaration are "injected" and those added afterwards are not made visible. THE SOLUTION ------------ Luckily there is a simple and very effective solution to this problem: it requires no new features but a clarification of the way that templates interact with namespaces. The idea is simple: if a template X is declared in namespace A, you may declare or define a specialisation X in namespace B provided the template A::X is visible. When you use X, two searches are performed. The first search looks for the template X and finds it in A, the second search then looks for any specialisations X and finds one in B. If no specialisation is found, the use is bound to the generated instance A::X. This mechanism allows multiple user specialisations to coexist in harmony without conflict, as well as allow use of unspecialised versions. EXAMPLE ------- // here two specialised and one unspecialised definitions // coexist peacefully template class X { .. } // original template ::X X xi1; // use of default, generated, or unspecialised version namespace user1 { class X { .. } // announce specialisation X xi2; // bind interface to ::X // bind definition to user1::X } namespace user2 { class X { .. } // announce specialisation X xi3; // bind interface to ::X // bind definition to user2::X } // the dual binding of interface and definition is // analogous to virtual functions, the use of namespaces // to inheritance Implementation: When an instance is used, search for a true declaration of the template first, ignoring specialisation declarations. Then search for specialisation declarations. Ensure the name of the namespace in which the specialisation is defined is mangled into its external linkage name. OPEN CLOSED PRINCIPLE --------------------- The open closed principle requires a module to be simultaneously closed so it can be used, and open so it can be extended. That classes and inheritance allow this is the keystone of class based object oriented languages. That declaration of specialisations in namespaces allows the original template to be closed and protected from interference while at the same time being open by permitting specialisations in namespaces is strong theoretical evidence that the proposal is coherent and sound. IMPLEMENTATION METHOD --------------------- Implementation is straightforward. Each use of a template instance needs to bound to a particular definition. For a function, that binding is performed as usual by use of an external name which has the namespace of the definition mangled into it. For a class, binding the interface is done by normal lookup and scope rules, binding the methods is again a matter of name mangling involving the appropriate namespace name. Since most compilers already supporting specialisations and namespaces probably do this anyhow, I dont see any problems with implementation here. DIFFICULTIES ------------ The proposal introduces a difficulty which has several possible resolutions. Consider: template class X { ..} namespace user { X xu1; // use unspecialised version class X { .. } // specialisation X xu2; // use specialised version } It does not appear consistent to allow X to mean two different things in the same namespace, however in fact it is consistent and existing practice in other circumstances: class A { .. } namespace user { A a1; class A { .. } A a2; } However consider: template void f(U) { ..} U u; f(u); // calls unspecialised version void f(U) { .. } // specialisation f(u); // calls specialisation While allowing the above code is consistent it requires two definitions of the same function in the same namespace. This can be implemented by marking the mangled names of specialisations and unspecialised generated definitions differently, but I dont consider it very nice. A compromise rule banning definition of a specialisation in the same namespace as the declaration of the template after use of an unspecialised version provides compatibility but is too ugly. It is not so easy to extend this solution to other namespaces: template class X { ..} namespace user { namespace inner { X xu1; // use unspecialised version } class X { .. } // specialisation namespace inner { X xu2; // use specialised version } } because here the specialisation is not in either the namespace of the template or the use of the instance X. A more general rule: "Every use of a template instance in a declarative region shall use the same specialisation or the unspecialised version" provides complete consistency, however. Unfortunately, breaches of this rule cannot always be diagnosed because namespaces are not encapsulated.