10.2 Lazy Instantiation

Ru-Brd

The examples so far illustrate requirements that are not fundamentally different from the requirements when using nontemplate classes. Many uses require a class type to be complete . For the template case, the compiler will generate this complete definition from the class template definition.

A pertinent question now arises How much of the template is instantiated ? A vague answer is the following: Only as much as is really needed. In other words, a compiler should be "lazy" when instantiating templates. Let's look at exactly what this laziness entails.

When a class template is implicitly instantiated, each declaration of its members is instantiated as well, but the corresponding definitions are not. There are a few exceptions to this. First, if the class template contains an anonymous union, the members of that union's definition are also instantiated. [3] The other exception occurs with virtual member functions. Their definitions may or may not be instantiated as a result of instantiating a class template. Many implementations will, in fact, instantiate the definition because the internal structure that enables the virtual call mechanism requires the virtual functions actually to exist as linkable entities.

[3] Anonymous unions are always special in this way: Their members can be considered to be members of the enclosing class. An anonymous union is primarily a construct that says that some class members share the same storage.

Default function call arguments are considered separately when instantiating templates. Specifically, they are not instantiated unless there is a call to that function (or member function) that actually makes use of the default argument. If, on the other hand, that function is called with explicit arguments that override the default, then the default arguments are not instantiated.

Let's put together an example that illustrates all these issues:

  // details/lazy.cpp  template <typename T>  class Safe {  };  template <int N>  class Danger {    public:      typedef char Block[N];  // would fail for  N<=0  };  template <typename T, int N>  class Tricky {    public:      virtual ~Tricky() {      }      void no_body_here(Safe<T> = 3);      void inclass() {          Danger<N> no_boom_yet;      }  //  void error() { Danger<0> boom; }  //  void unsafe(T (*p)[N]);      T operator->();  //  virtual Safe<T> suspect();      struct Nested {          Danger<N> pfew;      };      union {  // anonymous union  int align;         Safe<T> anonymous;      };  };  int main()  {     Tricky<int, 0> ok;  } 

First consider the previous example without the function main() . A standard C++ compiler normally compiles the template definitions to check the syntax and general semantic constraints. It will, however, "assume the best" when checking constraints involving template parameters. For example, the parameter N in the member typedef for Block could be zero of negative (which would be invalid), but it is assumed that this isn't the case. Similarly, the default argument specification ( = 3 ) on the declaration of the member no_body_here() is suspicious because the template Safe isn't initializable with an integer, but the assumption is that the default argument won't actually be needed for the generic definition of Safe<T> . If it weren't commented out, the member error() would trigger an error while the template is compiled because the use of Danger<0> requires a complete definition of the class Danger<0> , and generating that class runs into an attempt to typedef an array with zero elements! This is the case even though the member error() may not be used and therefore may not be instantiated. The error is triggered during the processing of the generic template. The declaration of the member unsafe(T (*p) [N]) , in contrast, is not a problem when N is still an unsubstituted template parameter.

Now let's analyze what happens when we add the function main() . It causes the compiler to substitute int for T and for N in the template Tricky . Not all the member definitions will be needed, but the default constructor (implicitly declared in this case) and the destructor are definitely called, and hence their definitions must be available somehow (which is the case in our example). In practice, the definitions of virtual members should also be provided; otherwise , linker errors are likely to occur. This may have been a problem if we had uncommented the declaration of the virtual member suspect() for which no definition was provided. The definitions of the members inclass() and struct Nested would need the complete type Danger<0> (which contains an invalid typedef as we discussed earlier) but because these definitions are not used, they are not generated, and no error is triggered. However, all the member declarations are generated, and these could contain invalid types as the result of our substitution. For example, if we uncommented the declaration of unsafe(T (*p) [N]) , we would again create an array type with zero elements, and this time it would be an error. Similarly, had the member anonymous been declared with type Danger<N> instead of Safe<T> , an error would be triggered because type Danger<0> cannot be completed.

Finally, we need to take note of operator-> . Normally, this operator must return a pointer type or another class type to which operator-> applies. This suggests that the completion of Tricky<int, 0> triggers an error because it declares a return type of int for operator-> . However, because certain natural class template definitions [4] trigger these kinds of definitions, the language rule was made more flexible. A user -defined operator-> must return only a type to which another (for example, builtin) operator-> applies if that operator is actually selected by the overload resolution rules. This is true even outside templates (although it is less useful in those contexts). Hence, the declaration here triggers no error, even though int is subsituted for the return type.

[4] Typical examples are so-called smart pointer templates (for example, the standard std::auto_ptr<T> ). See also Chapter 20.

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net