Could anyone provide a comparison or specific details of how is template instantiation handled at compile and/or link time in GCC and MS compilers? Is this process different in the context of static libraries, shared libraries and executables? I found this doc about how GCC handles it but I'm not sure if the information is still referring to the current state of things. Should I use the flags they suggest there when compiling my libraries e.g. -fno-implicit-templates?
What I know (might not necessarily be correct) is that:
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation to handle a specific set of template arguments is called a specialization.
To instantiate a template class explicitly, follow the template keyword by a declaration (not definition) for the class, with the class identifier followed by the template arguments. template class Array<char>; template class String<19>; When you explicitly instantiate a class, all of its members are also instantiated.
In order for any code to appear, a template must be instantiated: the template arguments must be provided so that the compiler can generate an actual class (or function, from a function template).
Template compilation requires the C++ compiler to do more than traditional UNIX compilers have done. The C++ compiler must generate object code for template instances on an as-needed basis. It might share template instances among separate compilations using a template repository.
templates will be instantiated when actually used
Not exactly, but roughly. The precise point of instantiation is a bit subtle, and I delegate you over to the section named Point of instantiation in Vandevoorde's/Josuttis' fine book.
However, compilers do not necessarily implement the POIs correctly: Bug c++/41995: Incorrect point of instantiation for function template
templates will be instantiated when actually used
That is partially correct. It is true for function templates, but for class templates, only the member functions that are used are instantiated. The following is well-formed code:
#include <iostream> template <typename> struct Foo { void let_me_stay() { this->is->valid->code. get->off->my->lawn; } void fun() { std::cout << "fun()" << std::endl; } }; int main () { Foo<void> foo; foo.fun(); }
let_me_stay()
is checked syntactically (and the syntax there is correct), but not semantically (i.e. it is not interpreted).
However, only dependent code is interpreted later; clearly, within Foo<>
, this
is dependent upon the exact template-id with which Foo<>
is instantiated, so we postponed error-checking of Foo<>::let_me_alone()
until instantiation time.
But if we do not use something that depends on the specific instantiation, the code must be good. Therefore, the following is not well-formed:
$ cat non-dependent.cc template <typename> struct Foo { void I_wont_compile() { Mine->is->valid->code. get->off->my->lawn; } }; int main () {} // note: no single instantiation
Mine
is a completely unknown symbol to the compiler, unlike this
, for which the compiler could determine it's instance dependency.
The key-point here is that C++ uses a model of two-phase-lookup, where it does checking for non-dependent code in the first phase, and semantic checking for dependent code is done in phase two (and instantiation time) (this is also an often misunderstood or unknown concept, many C++ programmers assume that templates are not parsed at all until instantiation, but that's only myth coming from, ..., Microsoft C++).
The definition of Foo<>::let_me_stay()
worked because error checking was postponed to later, as for the this
pointer, which is dependent. Except when you would have made use of
explicit instantiations
cat > foo.cc #include <iostream> template <typename> struct Foo { void let_me_stay() { this->is->valid->code. get->off->my->lawn; } void fun() { std::cout << "fun()" << std::endl; } }; template struct Foo<void>; int main () { Foo<void> foo; foo.fun(); } g++ foo.cc error: error: ‘struct Foo<void>’ has no member named ‘is’
When you explicitly instantiate, you instantiate explicitly. And make all symbols visible to the linker, which also means that the template definition may reside in different units of translation:
$ cat A.cc template <typename> struct Foo { void fun(); // Note: no definition }; int main () { Foo<void>().fun(); } $ cat B.cc #include <iostream> template <typename> struct Foo { void fun(); }; template <typename T> void Foo<T>::fun() { std::cout << "fun!" << std::endl; } // Note: definition with extern linkage template struct Foo<void>; // explicit instantiation upon void $ g++ A.cc B.cc $ ./a.out fun!
However, you must explicitly instantiate for all template arguments to be used, otherwise
$ cat A.cc template <typename> struct Foo { void fun(); // Note: no definition }; int main () { Foo<float>().fun(); } $ g++ A.cc B.cc undefined reference to `Foo<float>::fun()'
Small note about two-phase lookup: Whether a compiler actually implements two-phase lookup is not dictated by the standard. To be conformant, however, it should work as if it did (just like addition or multiplication do not necessarily have to be performed using addition or multiplication CPU instructions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With