Instantiating the templates at link time would probably make more sense. With LTO we're already deferring a significant portion of the compilation process to link time so this is an obvious extension.
This issue was also on the mind of the C++ standards committee when they began work on the new module system. I'm not sure how the current spec behaves with respect to template instantiation, though.
C++98 contained some provisions for that ("export template"), but anecdotally there is exactly one compiler that supports it and the whole mechanisms was found to cause more problems than it solves. See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n142...
Problem with template instantiation is that the mechanism is too general to be amendable to some kind of meaningful precomputation.
With Lisp you also need to have the macro definition ready when compiling code that depends on it. This is essentially the problem that all the various "system definition facilities" try to solve in at least somewhat usable manner.
You need the package definition for the source-code-read time of whatever you're compiling, and the macros for its macro-expansion time. Practically speaking, these are tied together.
It is not equally powerful, but Java's approach is easure. This means that at compile time the language can differentiate List<String> and List<Buffer>, and will type check them correctly, but at runtime the generic type gets "erased" and all the code turns into List<Object> (where in Java all types derive from Object).
As a result, there do not need to be multiple specializations of the List type for strings and buffers. No matter how many different parameterizations there are of a given generic class, there is only one implementation. I think this is probably a good thing most of the time, since on average the performance gains of this form of specialization are insignificant compared to the costs of code size. You wouldn't want 100 or more different implementations of List clogging memory and needing to be JIT-compiled.
One of Java's constraints is also that all generic types are reference types. So one reason why it wouldn't be too useful to instantiate separate List<String> and List<Buffer> is because these are basically just List<ReferenceToObject> anyway. C++ templates can provide both value and reference semantics, and can work on primitive types such as `int` which may reside on the stack, whereas Java generics only operate on full-fledged garbage collected objects that are dynamically allocated on the heap.
The disadvantage of this approach is that it precludes some constructs that are possible in other languages. For example, in C++ a template method could take some type `T` and construct a new instance of it. In C++ you could do this, but in Java it's not legal:
<T> T broken() { return new T(); }
In C++ you could also create a new local variable of type `T` that will be allocated on the stack. This `T` can represent anything from a primitive `int` to a complex struct or class, or a point or reference to a dyanmically allocated object. The C++ code could then dynamically allocate a new instance of `T`.
This makes it more difficult to use implement generic programming patterns analogous to things like std::allocator in C++, and to deliberately specify different implementations of parameterized types and algorithms. Fortunately these are not too significant of practical downsides in Java.
This issue was also on the mind of the C++ standards committee when they began work on the new module system. I'm not sure how the current spec behaves with respect to template instantiation, though.