When does implicit template creation occur?

It is interesting when / where implicit template creation occurs in the following situation.

// temp.h template <typename T> struct A { T value; } 
 // foo.h #include "temp.h" void foo(); 
 // foo.cpp #include "foo.h" void foo() { A<int> _foo; } 
 // bar.h #include "temp.h" void bar(); 
 // bar.cpp #include "bar.h" void bar() { A<int> _bar; } 
 // main.cpp #include "foo.h" #include "bar.h" int main() { foo(); bar(); return 0; } 

I think this happens when foo() is called, because this is the first use of A<int> , so A<int> implemented in foo.o
And, when bar() is called, it is bound to A<int> in foo.o

I'm right? Or does instantiation happen twice?

+7
c ++
source share
2 answers

The standard says nothing about how the compiler should create a template implicitly.

I'm not sure about the compiler, since g ++ works with it. 7.5 Where is the template? :

Somehow, the compiler and linker must make sure that each instance of the template occurs exactly once in the executable file, if necessary, and not otherwise. There are two main approaches to this problem, which are called the Borland model and the Cfront model.

  • Borland Model:

Borland C ++ solved the problem of creating a template by adding the code equivalent of common blocks to their linker; the compiler emits template instances in each translation unit that uses them, and the linker collapses them together. The advantage of this model is that the linker should only consider the object files themselves; there is no external complexity to worry about. The disadvantage is that compilation time is increased because the template code is compiled again. The code written for this model typically includes definitions of all the templates in the header file, since they should be treated as created.

  • Cfront Model:

The AT&T C ++ translator, Cfront, solved the problem of creating a template by creating the concept of a template repository, an automatically supported place where template instances are stored. A more modern version of the repository works as follows: as individual object files are created, the compiler places any template definitions and instances found in the repository. During the connection, the link wrapper adds objects to the repository and compiles all the necessary instances that were not previously selected. The advantages of this model are a more optimal compilation speed and the ability to use the system linker; to implement the Borland model, the compiler provider must also replace the linker. The disadvantages are significantly increased complexity and, therefore, the likelihood of error; for some code this may be just as transparent, but in practice it would be very difficult to create several programs in one directory and one program in several directories. The code written for this model tends to separate the definitions of non-built-in element templates into a separate file that must be compiled separately.

This is how g ++ implements it, my emphasis is:

g ++ implements the Borland model for purposes that the linker supports, including ELF targets (such as GNU / Linux), Mac OS X, and Microsoft Windows. Otherwise, g ++ does not implement any automatic model.

That said: with g ++, each translation unit will have its own instance. This was mentioned again on this page:

... but each translation unit contains instances of each of the patterns used. Duplicated instances will be discarded by the linker, but in a large program this can lead to an unacceptable amount of code duplication in object files or shared libraries.

Your option to avoid this (of course, the first and last option is explicit):

  • Duplicating template instances can be avoided by specifying explicit instance creation in a single object file and preventing the compiler from executing implicit instances in any other object files using an explicit creation declaration using the extern template syntax

  • Compile your template with -frepo . The compiler creates files with the extension .rpo , listing all the template instances used in the corresponding object files that can be created there; the link wrapper collect2 then updates the .rpo files to tell the compiler where to place these instances and rebuild any affected object files. The overhead of link time is negligible after the first pass, as the compiler continues to place instances in the same files.

  • Compile your code with -fno-implicit-templates to disable the implicit generation of template instances and explicitly create all the ones you use.

+3
source share

According to the GNU C ++ compiler documentation ( https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html ) "each translation unit contains instances of each of the templates it uses." Therefore, if your unused _foo and _bar objects are optimized, both object files must have duplicate instances. Then "duplicate instances will be discarded by the linker", which usually means that the order of the links determines which instance is used. But the end result is still not going to change in either of two orders.

+2
source share

All Articles