Templates and separate compilation

前端 未结 2 1109
梦谈多话
梦谈多话 2021-01-06 02:39

I want to write a program in C++ with separate compilation and I wrote this:

main.cpp

#include 
#include \"Stack.h\"
using namespace          


        
相关标签:
2条回答
  • 2021-01-06 03:13

    Template classes need to have the method definitions inside the header file.

    Move the code you have in the .cpp file inside the header, or create a file called .impl or .imp, move the code there, and include it in the header.

    The compiler needs to know the method definitions to generate code for all specializations.

    Before you ask, no, there is no way to keep the implementation outside the header.

    0 讨论(0)
  • 2021-01-06 03:19

    I would say it will be more pragmatic to first understand how separate compilation works for normal (untempelated) files and then understand how g++ compiler does it for template.

    First in normal files, when the header file containing only the declarations are #included in main file, the preprocessor replaces the declarations from the header and puts it to the main file. Then after the preprocessing phase is over, the compiler does one by one compilation of the pure C++ source code contained in .cpp files and translates it into object file. At this point the compiler doesn't mind the missing definitions (of functions/classes) and the object files can refer to symbols that are not defined. The compiler, hence can compile the source code as long as it is well formed.

    Then during the linking stage the compiler links several files together and it is during this stage the linker will produce error on missing/duplicate definitions. If the function definition is correctly present in the other file then the linker proceeds and the function called from the main file is successfully linked to the definition and can be used.

    For templates, things work differently. It will be illustrative to consider an example, so I pick a simple one:

    consider the header file for template array class:

    array.h

    #ifndef _TEMPLATE_ARRAY_H_
    #define _TEMPLATE_ARRAY_H_
    template <class T>
    class Array
    {
       private:
            T *m_list;
            int m_length;
    
       public:
            Array() //default constructor
            {
               m_list = nullptr;
               m_length = 0;
            }
    
    
            Array(int length)
            {
              m_list = new T[length];
              m_length = length;
            }
    
            ~Arrary()
            {
                delete[] m_list;
                m_list = nullptr;
            }
    
          //undefined functions
           int getLength();
           T getElement(const int pos);
    };
    

    and the corresponding array.cpp file :

    include "array.h"
    
    template <class T>
    array<T>::getLength()
    { return m_length; }
    
    template <class T>
    T Array<T>::getElement(const int pos)
    { return m_list[pos]; }
    

    Now consider the main file where two instances of the templated object array, one for int and another for double is created.

    main.cpp

    #include "array.h"
    #include <iostream>
    
    
    int main()
    {
        Array<int> int_array;
        Array<double> double_array;
    
       std::cout << int_array.getLength() <<"\n";
       std::cout << double_array.getLength() << "\n";
    }
    

    When this piece of code is compiled, the preprocessor first copies the template declarations from the header file to the main file as usual. Because in the main file Array< int > and Array< double > objects are instantiated, compiler instantiates two different definitions of Array class, one each for double and int and then instantiate the Array objects in the main.cpp file.

    Note till this point the function definitions for Array< int >::getLength() and Array< double >::getLength() is still missing in the main.cpp file but since the source code is well formed the compiler compiles the main.cpp file without any hassle. In short there's no difference b/w templated object/function compilation and non-templated function compilation till now.

    In the meanwhile the code file for array.cpp containing the template function definitions for Array< T >::getLength() and Array< T >::getElement() is compiled, but by this time the compiler would have forgotten that main.cpp needs Array< int >::getLength() and Array< double >::getLength() and would happily compile the code array.cpp without generating any instances for int and double version of the function definition needed by the main.cpp file. (Remember that compiler compiles each file separately!)

    It is during the linking phase horrible template errors start popping because of the missing function definitions for int and double version of template function definition that are required by the main file. In the case of non-template declarations and definitions, the programmer makes sure to define the sought function in a file which can be linked together with the file calling the function. But in the case of templates, the linker which executes after the compilation phase, cannot do a task that a compiler is suppose to do, i.e generate a code, in this case for int and double type of the template function

    There are ways to get around this

    Having gone through the entire story, one can easily conclude that the entire fuss up around template separate compilation is due to linkage (i.e) if all codes are written correctly, class and functions declared in header and defined in another separate file). Ways of getting around this are :

    1. Define the class and functions in the header files themselves rather than in separate file so that the contents of header file when included in the main file, includes the templated definitions which cause appropriate instances of necessary functions to be defined by the compiler.

    2. Instantiate the type definitions you know you will need in the separate file where the template definitions are written. This will then directly be linked to the function calls in the main file.

    3. Another way to get around this is to name the .cpp file where definitions are written to .inl* file (from the e.g drawn above, chagne array.cpp to array.inl); inl means inline and include the .inl file from the bottom of the header file. This yields the same result as defining all functions within the header file but helps keeping the code a little cleaner.

    4. There's another way, i.e #include .cpp file with templated definitions in the main file which I personally don't prefer because of non-standard usage of #include.

    0 讨论(0)
提交回复
热议问题