From: http://www.cppblog.com/michaelgao/archive/2008/10/09/63571.html
Why does the C ++ compiler not support separate template compilation?
Liu weipeng (pongba)/Wen
First, as mentioned in the C ++ standard, a compilation unit [Translation Unit] refers to. CPP file and all. h file ,. the code in the H file will be extended to include it. in the CPP file, and then the compiler compiles. CPP file is. OBJ file, which has the PE [portable executable, that is, Windows Executable File] file format, and contains binary code, but it may not be executed, because it is not guaranteed that there must be a main function. Hosts file.
For example:
// --------------- Test. h -------------------//
Void F (); // declare a function f
// --------------- Test. cpp --------------//
# I nclude "test. h"
Void F ()
{
... // Do something
} // The F function declared in test. H is implemented here.
// --------------- Main. cpp --------------//
# I nclude "test. h"
Int main ()
{
F (); // call F, F has an external connection type
}
In this example, test. CPP and Main. CPP is compiled into different ones. OBJ file [name it test. OBJ and Main. OBJ], in Main. in CPP, F function is called. However, when the compiler compiles main. only main. test. the declaration of void F (); in the H file. Therefore, the compiler regards F as an external connection type, that is, its function implementation code is in another. in the OBJ file, this example is test. OBJ, that is, Main. OBJ does not actually have a line of binary code about the f function, and the Code actually exists in test. test. OBJ. In
Calling F in Main. OBJ generates only one line of Call Command, as shown in the following code:
The name in call f [C ++ is of course mangling [Processed]
During compilation, this call command is obviously incorrect, because main. OBJ does not have a line of F implementation code. What should we do? This is the task of the connector. The connector is responsible for other tasks. in OBJ [this example is test. OBJ] Find the implementation Code of F and replace the call address of the call f command with the actual entry point address of the f function. Note that, on the connector, The .obj.pdf in the project is connected to a. EXE file, and the most critical task is to find an External Connection Symbol in another. OBJ address, and then replace the original "false" address.
This process is more in-depth:
The call f command is actually not like this. It is actually a so-called stub, that is,
JMP 0x23423 [this address may be arbitrary, but the key is that there is a line of instruction on this address to perform the real call f action. That is to say, all calls to F in this. OBJ file are directed to the same address by JMP, and the latter only truly calls "F. The advantage of this is that when the connector modifies the address, you only need to modify the call XXX address of the latter. The alias format is the same. In such a file, there is a symbolic import table and a symbolic export table [import table and export table] in which all the symbols are associated with their addresses. In this way, the connector only needs to be in the test. in the OBJ symbol export table, find the address of the symbol F [of course C ++ has done mangling for F]. Then, after some offset processing, [because two. OBJ file merge, of course, the address will be offset, this connector is clear] write into main. OBJ symbol import table F
The one occupied.
This is the general process. The key is:
When compiling main. CPP, the compiler does not know the implementation of F. All calls to it only give an indication that the connector should look for the implementation body of F for it. That is to say, Main. OBJ does not have any line of binary code about F.
When compiling test. CPP, the compiler finds the implementation of F. As a result, the implementation of F [binary code] appears in test. obj.
During connection, the connector finds the address of the implementation code [binary] of F in test. OBJ [export table by symbol]. Then, change the pending call XXX address in Main. OBJ to the actual address of F.
Complete.
However, you know that the code of the template function cannot be directly compiled into binary code, and there must be a "active" process. For example:
// ---------- Main. cpp ------//
Template <class T>
Void F (t)
{}
Int main ()
{
... // Do something
F (10); // call F <int> the compiler decides here to give F a cash body of F <int>
... // Do other thing
}
That is to say, if you have not called F in the main. cpp file, F will not be available, so there will be no line of binary code about F in Main. OBJ !! If you call:
F (10); // F <int> can be activated
F (10.0); // F <double> can be activated
In this way, F <int> and F <double> are included in Main. obj. And so on.
However, the compiler is required to understand the template definition, right?
See the following example: [separating the template from its implementation]
// ------------- Test. h ----------------//
Template <class T>
Class
{
Public:
Void F (); // here is just a declaration
};
// --------------- Test. cpp -------------//
# I nclude "test. h"
Template <class T>
Void a <t>: F () // template implementation, but note:
{
... // Do something
}
// --------------- Main. cpp ---------------//
# I nclude "test. h"
Int main ()
{
A <int>;
A. F (); // The Compiler does not know the definition of a <int>: F, because it is not in test. h.
// So the compiler had to hope for the connector, hoping it could be found in other. OBJ
// A <int>: The implementation body of F. In this example, It is test. obj. However, the latter actually has a <int >:: F
// Binary code? No !!! The C ++ standard clearly indicates that when a template is not used
// Shouldn't it be discovered, and a <int>: F is used in test. cpp? No !! Therefore
// In the test. OBJ file compiled by test. CPP, the binary code of A: F does not exist either.
// The connector is dumpfounded, so a connection error is returned.
// However, if. write a function in CPP, and call a <int>: F, the compiler will display it //, because [test. in CPP], the compiler knows the template definition, so it can be // active, so, test. in the OBJ symbol export table, the location of the <int>: F symbol is available.
// Address, so the connector can complete the task.
}
The key is: in a separate compilation environment, the compiler compiles a file. the CPP file does not know another one. the existence of the CPP file does not look for [it is expected to be a connector when there is a pending symbol]. This mode runs well without a template, but it is dumpfounded when it comes to a template, because the template is only available when necessary. Therefore, when the compiler only sees the template declaration, it cannot present the template. It can only create a symbol with an external connection and expects the connector to determine the address of the symbol. However, when this template is implemented. when the template is not used in the CPP file, the compiler is too lazy to use the template. Therefore, the entire project. OBJ cannot find a line of binary code with the template as the entity, so the connector is
/////////////////////////////////
Http://dev.csdn.net/develop/article/19/19587.shtm
How the C ++ template code is organized-the inclusion model selects the blog from sam1111
Keyword: Template extension sion Model
Source: C ++ template: the complete guide
Note: This article is translated from part of chapter 1 of "C ++ template: the complete guide. Recently I have seen many posts on the C ++ Forum about the template inclusion mode. When I think of my new template, I am also confused about similar problems. Therefore, I have translated this article, hope to help beginners.
The template code has several different organization methods. This article introduces one of the most popular methods: the inclusion mode.
Link error
Most C/C ++ programmers organize their non-template Code as follows:
· All classes and other types are placed in header files, which have the. HPP (or. H,. H,. HH,. hxx) extension.
· For global variables and (non-inline) functions, only declarations are placed in the header file, and definitions are placed in the point c file. These files have. CPP (or. C ,. C ,. CC ,. cxx) extension.
This organization method works well: it makes it easy to access the required Type Definitions during programming, and avoids the error of "variable or function repeated definition" from the linker.
Due to the influence of the above Organization Conventions, new users of template programming often make the same mistake. The following short program shows this error. As with "Common Code", we define a template in the header file:
// Basics/myfirst. HPP
# Ifndef myfirst_hpp
# Define myfirst_hpp
// Declaration of Template
Template <typename T>
Void print_typeof (T const &);
# Endif // myfirst_hpp
Print_typeof () declares a simple helper function to print some type information. The function definition is stored in the point c file:
// Basics/myfirst. cpp
# I nclude <iostream>
# I nclude <typeinfo>
# I nclude "myfirst. HPP"
// Implementation/definition of Template
Template <typename T>
Void print_typeof (T const & X)
{
STD: cout <typeid (x). Name () <STD: Endl;
}
This example uses the typeid operator to print a string, which describes the type information of the input parameter.
Finally, we use our template in another point c file. In this file, the template declaration is # I nclude:
// Basics/myfirstmain. cpp
# I nclude "myfirst. HPP"
// Use of the template
Int main ()
{
Double ice = 3.0;
Print_typeof (ICE); // call function template for Type Double
}
Most C ++ compilers (compiler) are likely to accept this program. There is no problem, but the linker may report an error stating that the print_typeof () function is missing.
The reason for this error is that the template function print_typeof () has not been defined as instantiate ). To make a template available, the compiler must know which definition should be made available, and what template parameters should be used to make it available. Unfortunately, in the previous example, the two groups of information exist in different files compiled separately. Therefore, when our compiler sees the call to print_typeof (), but does not see that this function is a modern definition of the double type, it just assumes that such a definition is provided elsewhere, and create a reference for that definition (the linker uses this reference for parsing ). On the other hand, when the compiler processes myfirst. CPP, the file does not have any indication that it must be defined for the special parameters it contains.
Template in header file
The general solution to this problem is to use the same method as using macro or inline functions: we include the template definition into the header file of the Declaration template. For our example, we can add # I nclude "myfirst. CPP "added to myfirst. the end of the HPP file, or the point c file that uses our template contains myfirst. CPP file. Of course, the third method is to delete the myfirst. cpp file and rewrite the myfirst. HPP file so that it contains all template declarations and definitions:
// Basics/myfirst2.hpp
# Ifndef myfirst_hpp
# Define myfirst_hpp
# I nclude <iostream>
# I nclude <typeinfo>
// Declaration of Template
Template <typename T>
Void print_typeof (T const &);
// Implementation/definition of Template
Template <typename T>
Void print_typeof (T const & X)
{
STD: cout <typeid (x). Name () <STD: Endl;
}
# Endif // myfirst_hpp
This method of organizing template code is called the include mode. After such adjustments, you will find that our program has been correctly compiled, linked, and executed.
We can get some observations from this method. One of the most noteworthy points is that this method adds overhead containing myfirst. HPP to a considerable extent. In this example, this overhead is not caused by the size defined by the template, but by the fact that we must include the header files used by our template, in this example, <iostream> and <typeinfo> are used. You will find that this eventually leads to thousands of lines of code, because header files such as <iostream> also contain template definitions similar to ours.
This is indeed a problem in practice because it increases the time required by the compiler to compile an actual program. Therefore, we will verify other possible methods in subsequent chapters to solve this problem. But in any case, it takes an hour for programs in the real world to compile the Link (we once encountered a program that took several days to compile from the source code ).
Aside from the Compilation Time, we strongly recommend that you organize template code according to the inclusion mode whenever possible.
Another observation is that the most important difference between a non-inline template function and an inline function and a macro is that it is not expanded on the caller end. On the contrary, when the template function is activated, a new copy of the function is generated. Because this is an automatic process, the compiler may generate two identical copies in different files, causing the linker to report an error. Theoretically, we do not care about this: This is something that compiler designers should care about. In fact, everything works normally most of the time and we don't have to deal with it. However, for large projects that need to create their own libraries, this problem occasionally appears.
Finally, it should be pointed out that in our example, the method applied to common template functions is also applicable to the member functions, static data members, and template member functions of the template class.