How to Solve Undefined Reference to Template Class Error of C++

How to Solve Undefined Reference to Template Class Error of C++

·

3 min read

Recently, in my open-source project Num.NET, a problem puzzled me.


The problem

I abstract my code as below:

define.h

template<typename T>
class Methods{
public:
    T methodA(T inp);
    void methodB(vector<T> &inp, int c);
}

impl.cpp

template<typename T>
T Methods<T>::methodA(T inp){
    // do something to implement it
}

template<typename T>
void Methods<T>::methodB(vector<T> &inp, int c){
    // do something to implement it
}

call.cpp

void call(){
    int inp1 = 15;
    Methods<int> m1;
    inp1 = m1.methodA(inp1);

    vector<float> inp2(10);
    Methods<float> m2;
    m2.methodB(inp2, 99);
}

Then, when linking, I got errors such as Undefined reference to Methods<int>::methodA(int), Undefined reference to Methods<float>::methodB(vector<float>, int).

The point is that when compiling the call.cpp, the compiler generates two classes which are Methods<int> and Methods<float> in the translating unit of call.cpp and define.h. However, in the other unit which consists of call.cpp and define.h, the compiler cannot get the information of the specifying of Methods<int> and Methods<float>. So in this unit, neither function ofMethod<int> nor function of Methods<float> is compiled.

Unfortunately, compiler does not throw out an error because all things are valid during this period.

However, when the linker begins to work, the problem will be exposed.

When linking the two units, the linker tries to ask for implementation of Methods<int> and Methods<float>. Obviously, there is no such implementation. That is why the linker throws out a mistake called Undefined reference to xxx


Solutions

1. Specify the implementations of functions manually

I believe that this is the solution we can think of at first. Since the linker failed to find implementation of the functions, we could just give out all the functions the linker needs, including Methods<int>::methodA, Methods<int>::methodB, Methods<float>::methodA, Methods<float>::methodB.

However, this is a little troublesome, especially when the implementation is like the code below. (The code itself has some potential problems, but we just use it as an example.)

impl.cpp
template<typename T>
void Methods<T>::methodB(vector<T> &inp, int c){
    if(c >= inp.size()) return;
    std::cout << inp[c];
}

If you choose this solution, you need to write such code:

impl.cpp

template<typename int>
void Methods<int>::methodB(vector<int> &inp, int c){
    if(c >= inp.size()) return;
    std::cout << inp[c];
}

template<typename float>
void Methods<float>::methodB(vector<float> &inp, int c){
    if(c >= inp.size()) return;
    std::cout << inp[c];
}

Ohh....the code are so similar but we may repeat them many times. I think this is the disadvantage of this solution.

2. Put the implementations in the header file

Well, this is a simple but discouraging solution. Putting implementations in the header could directly avoid the error when linking because the implementations are exactly in the unit of call.cpp and define.h.

However, when your code becomes more large-scaled and complex, you will have a difficult time managing your code.

3. Specify the template class in the header or implementation file

We can also just do like this:

define.h

template class Methods<int>;
template class Methods<float>;

or

impl.cpp

template class Methods<int>;
template class Methods<float>;

Thus the compiler could know that they need to generate two versions of implementation of the functions in Methods<T>.

This is the way I think best. It is clean and effective.