TITLE: "points of instantiation" (Newsgroups: comp.lang.c++.moderated, 7 Feb 98) SZONYE: "Bradd W. Szonye" > >int foo(); > >template struct A { int goo(); }; > >template struct B: A > > { int bar() { return foo(); } }; > >template <> struct A { int foo(); }; > > > >Which foo() should B::bar() call? Global ::foo() or > >A::foo()? Put the specialization of A before the > >definition of B, and it's clear that you should call > >A::foo(). HARRISON: Doug Harrison > Thanks, I hadn't considered that case. I was under the impression that > one could derive B from A without having to worry about A > specializations that could follow the definition of B. Above, > suppose B defined a function "void fun()", absent from A, but > present in A as "virtual void fun()". Does B's fun > override it? VANDEVOORDE: David Vandevoorde It all depends on the ``points of instantiation for A'' in your program. Let me digress in a somewhat simplified discussion of ``point of instantiation'' ... The ``point of instantiation'' (POI) concept is a very important one. There are really two sorts of instantiations to talk about: (1) instantiation of things that eventually occupy code or data memory; and (2) instantiation of ``other things''. (1) consists of: (a) function and member function definitions (code), (b) default function arguments (code), (c) static data member definitions (data & possibly initialization code). (2) includes declarations (e.g., prototypes) and class definitions. Type (2) instantiations must be complete _before_ the point in the source code where the instantiated entity is needed. Specifically, the entity is ``injected'' in namespace scope. For example: template struct S { /* ... */ }; // <- POI of S void f() { // <- not here because not namespace scope S s; } // Here would be too late Clearly, if the (implicit) specialization S were created after the point where it is needed in the definition of f(), it would come too late. I.e., we cannot ``back-patch'' the definition of f() to use the definition of S that follows it. Type (1) instantiations are completed _after_ the point in the source where the enitity is needed -- again in namespace scope. Example: template void f(T) { // ... } // <- POI of the _declaration_ of f(int) void g() { f(42); } // <- POI of the _definition_ (body) of f(int) Why this requirement? Because of the possible need to instantiate a template function that calls the function creating the point of instantiation: template void f(T a) { // ... g(a); // g dependent name // ... -> not looked up until instantiation }; // <- (*) void g(int) { f(8); } If the body of f(int) were created (instantiated) at point (*), there is a good chance that an error would occur because g(int) is not yet visible at that point (assuming there was no prior declaration of g(int)). Also note that the instantiation of a specialization of a class template creates POIs for the definition of each virtual function member of that class template. Finally, POIs are also created by explicit instantiation directives such as: template struct S; <- POI for S Now back to whether specializations override or not (your question above). They do, but for that mechanism to work correctly, the specialization _must_ appear before any POI of that specialization. This prompts I believe the following rule-of-thumb: ! (1) A partial specialization should appear as soon as ! possible after its primary template. It should be ! placed in the same file (or a mechanism should be ! used to prevent separate inclusion of both elements). ! ! (2) An explicit specialization should either follow ! (1) above, or appear as soon as possible after a ! declaration of a type that is a template-argument ! for that specialization. In both cases I'm talking about the _declaration_ of the specialization. The idea of this rules is to prevent inadvertent creation of POIs between primary templates and user-defined specializations. Rule (2) above enables the ``traits'' technique, I believe. (please correct me if you see a problem with the above.) You'd also want a similar characterization for overloaded function templates, but that is a bit harder because those things don't have the concept of a ``primary template''. And then, perhaps the hardest part of using templates: there can be (and often are) multiple POIs for a single specialization in a program. ! Every POI of a given (implicit) specialization must result ! in indentical bindings. A violation of that rule is a violation of the ODR (one- definition rule) which compilers are not always required to diagnose (and it is difficult to diagnose them in general). E.g., // hdr.h: template void f(T a) { g(a); // Dependent name } // file_1.C: #include "hdr.h" void g(double); void p1() { f(0); } // indirectly calls g(double) !! // <- POI for f(int) // file_2.C: #include "hdr.h" void g(int); void g(double); void p2() { f(0); } // indirectly calls g(int) !! // <- POI for f(int) Since the call `g(a)' binds differently for the two POIs of `f(int)' the program above has undefined behavior. This sort of argues against most forms of implicit conversions and for some kind of ``atomicity'' in the declaration of overloaded function sets. I suppose the stuff I just wrote may appear a little daunting at first, but most of the ideas make sense I think. So after playing with them for a short while they shouldn't be too hard to remember. Not being burned by their implications may be tougher in large projects.