c++模板類的使用,編譯的問題

xutopia發表於2021-12-21

1,模板類編譯的問題

前兩天在寫程式碼時,把模板類的宣告和分開放在兩個檔案中了,類似於下面這樣:

stack.hpp:

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};
#endif

stack.cpp:

#include <iostream>
#include "stack.hpp"

template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}

template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}

main.cpp

#include "stack.hpp"

int main() {
    stack<int> s;

    return 0;
}

編譯

$ g++ -c -o main.o main.cpp  
$ g++ -c -o stack.o stack.cpp   
$ g++ -o main main.o stack.o  
main.o: In function `main':  
main.cpp:(.text+0xe): undefined reference to 'stack<int>::stack()'  
main.cpp:(.text+0x1c): undefined reference to 'stack<int>::~stack()'  
collect2: ld returned 1 exit status  
make: *** [program] Error 1  

提示找不到函式的定義

在網上尋找的答案如下:

It is not possible to write the implementation of a template class in a separate cpp file and compile. All the ways to do so, if anyone claims, are workarounds to mimic the usage of separate cpp file but practically if you intend to write a template class library and distribute it with header and lib files to hide the implementation, it is simply not possible.

To know why, let us look at the compilation process. The header files are never compiled. They are only preprocessed. The preprocessed code is then clubbed with the cpp file which is actually compiled. Now if the compiler has to generate the appropriate memory layout for the object it needs to know the data type of the template class.

Actually it must be understood that template class is not a class at all but a template for a class the declaration and definition of which is generated by the compiler at compile time after getting the information of the data type from the argument. As long as the memory layout cannot be created, the instructions for the method definition cannot be generated. Remember the first argument of the class method is the 'this' operator. All class methods are converted into individual methods with name mangling and the first parameter as the object which it operates on. The 'this' argument is which actually tells about size of the object which incase of template class is unavailable for the compiler unless the user instantiates the object with a valid type argument. In this case if you put the method definitions in a separate cpp file and try to compile it the object file itself will not be generated with the class information. The compilation will not fail, it would generate the object file but it won't generate any code for the template class in the object file. This is the reason why the linker is unable to find the symbols in the object files and the build fails.

Now what is the alternative to hide important implementation details? As we all know the main objective behind separating interface from implementation is hiding implementation details in binary form. This is where you must separate the data structures and algorithms. Your template classes must represent only data structures not the algorithms. This enables you to hide more valuable implementation details in separate non-templatized class libraries, the classes inside which would work on the template classes or just use them to hold data. The template class would actually contain less code to assign, get and set data. Rest of the work would be done by the algorithm classes.

具體原因就是:

模板類其實就不是一個類,c++的編譯器在編譯.cpp產生二進位制目標檔案的時候,需要根據函式的引數型別來確定連結符號(編譯器不編譯.h檔案),而編譯模板類的時候因為函式的引數型別都沒有確定,所以也就不能產生連結符號,所以在編譯階段是不會報錯的,但是在連結階段就報錯了。針對這種情況有三種解決辦法,但是最優的還是把實現和宣告都放在標頭檔案中。如果不想讓c++類顯得臃腫,可以在類裡面宣告,在類外進行實現。

參考連結

https://stackoverflow.com/questions/1724036/splitting-templated-c-classes-into-hpp-cpp-files-is-it-possible

https://www.zhihu.com/question/20630104

https://www.bbsmax.com/A/rV573nLE5P/

https://blog.csdn.net/bangdingshouji/article/details/72832869?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.control

相關文章