Parsing of Link error in template function Compilation

Source: Internet
Author: User

Conclusion:

Place the full definition of the template function or template class in the. h file and add it to the. cpp file to use these template functions.

 

1. symptom description

Similar to references [1], when using a template function in the following way, a link error occurs due to template function declaration and definition separation:

1 // File "foo.h"2 template<typename T>3 extern void foo();
1 // File "foo.cpp"2 #include <iostream>3 #include "foo.h"4 5 template<typename T>6 void foo()7 {8   std::cout << "Here I am!\n";9 }

Use Time:

1 // File "main.cpp"2 #include "foo.h"3 4 int main()5 {6   foo<int>();7   ...8 }

In this case, a link error is reported.

Generally, there are two solutions:

(1) Place the complete definition of the template function or template class in the. h file and add it to the .cpp file to use the template function(of course, this method is fixed, which will generate the largest. EXE file for Some compilers );

(2) when defining a template function in the. cpp file, the template function is first instantiated, for example:

 1 // File "foo.cpp" 2 #include <iostream> 3 #include "foo.h" 4  5 template<typename T> void foo() 6 { 7   std::cout << "Here I am!\n"; 8 } 9 10 template void foo<int>();

Or: add the. cpp file to the. cpp file that requires the template function as the header file:

1 // file "Main. CPP "2 # include" foo. CPP "// it may be a bit confusing-this is the disadvantage of this method 3 Template void Foo <int> (); 4 5 Int main () 6 {7 Foo <int> (); 8... 9}

 

2. Principles

The references [3] already provide a good description. Here we will only repost them.

If you don't know how to solve the problem above, you can start Google's solution:The template does not support separate compilation. Place the declaration and implementation of your template class in the. h file.. After merging the. h and. cpp files according to this, you can.

But why? Why does the template not support separate compilation? --- Continue Google ing

Found the following article (original article link: http://blog.csdn.net/bichenggui/article/details/4207084 ):

First,Compilation Unit(Translation Unit) refers to. CPP file and all of its # include. 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 (Assume that our platform isWin32), The latter has the PE (portable executable, that is, Windows executable files) file format, and itself contains binary code, but not necessarily can 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--------------// #include”test.h” void f() { //do something } // Implement the f function declared in test. h. //---------------main.cpp--------------// #include”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. OBJ file (named 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, In Main. OBJ, there is actually no line of binary code about the f function, and the Code actually exists in the test. OBJ compiled by test. cpp. Calling F in Main. OBJ generates only one line of Call Command, as shown in the following figure:

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. OBJ (in this example, test. OBJ) Find the implementation Code of F, find and replace the call address of the call f command with the actual F function entry point address. 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 line is actually not like this. It is actually called stub, that is, a JMP 0 xabcdef. This address may be arbitrary, but the key is that there is a line of command 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 format of the Delimiter 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), and then perform some offset processing (because the two. OBJ file merge, of course, the address will be offset, this connector is clear) write into main. in OBJ, You can import the symbol that f occupies.

This is the general process. The key is:

When compiling main. CPP, the compiler does not know the implementation of F. Therefore, when it comes to calling it, it only gives 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. So F's implementation (Binary Code) appears in test. obj.

During connection, the connector finds the address of F's implementation code (Binary) in test. OBJ (export the table by symbol ). Then, change the pending call XXX address in Main. OBJ to the actual address of F. Complete.

However,TemplateAs you know, the code of the template function cannot be directly compiled into binary code. There must be an "instantiation" process. For example:

//----------main.cpp------// template<class T> void f(T t) {} int main() { //do something f(10); // Call F <int> the compiler decides to give f an instance of F <int>. //do other thing }

That is to say, if you have not called F in the main. cpp file, F willNot instantiatedSo that no line of binary code about F exists in Main. OBJ! If you call:

F (10); // F <int> can be instantiated.

F (10.0); // F <double> can be instantiated.

In this way, F <int> and F <double> are included in Main. obj. And so on.

However, instantiation requires the compiler to know the template'sDefinitionIsn't it?

Let's take a look at the example below (separate the template declaration and implementation ):

//-------------test.h----------------// template<class T> class A { public: void f(); // Here is just a declaration }; //---------------test.cpp-------------// #include”test.h” template<class T> void A<T>::f() // Template implementation { //do something } //---------------main.cpp---------------// #include”test.h” int main() { A<int> a; f(); // #1 }

The compiler does not know the definition of a <int>: F at #1, because it is not in test. h, so the compiler had to send it to the connector, hoping that it could be in another. find the <int>: F instance in OBJ. In this example, It is test. OBJ. However, is there a <int>: F binary code in the latter? No !!!The C ++ standard clearly states that,When a template is not used, it should not be instantiated., Test. cpp used a <int>: F? No !! SoIn fact, the binary code of A: F does not exist in the test. OBJ file compiled by test. cpp, so the connector is dumpfounded and has to give a connection error.However, if. write a function in CPP, and call a <int>: F, the compiler will instantiate it, because at this point (test. in CPP), the compiler knows the template definition, so it can be instantiated, so, test. the OBJ symbol export table has the address of a <int>: F, 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. CPP file exists, and it will not be searched (when there is a pending symbol, it will be sent to the connector ). This mode runs well without a template, but it is dumpfounded when it comes to a template, because the template will only be instantiated when needed. Therefore, when the compiler only sees the declaration of the template, it cannot instantiate 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 templates are not used in the CPP file, the compiler is too lazy to instantiate them. OBJ cannot find the binary code of a single template instance, so the connector is poor.

 

Reference

[1] How can I avoid linker errors with my template functions? Http://www.parashift.com/c++-faq/separate-template-fn-defn-from-decl.html

[2] C ++ template, linking error. http://stackoverflow.com/questions/1353973/c-template-linking-error

[3] C ++ template function Declaration definition separation compilation error details. http://www.cnblogs.com/qlwy/archive/2012/03/21/2410045.html

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.