Undefined Reference error when trying to use a template class

Asked

Viewed 492 times

1

I have a class template Set and a menu class both with their respective . cpp and . hpp, when trying to use a cluster class pointer in my Menu class I get the following error:

||=== Build: Debug in Trabalho04 (compiler: GNU GCC Compiler) ===|
obj\Debug\src\Menu.o||In function `ZN4MenuC2Ev':|
C:\Trabalho04\src\Menu.cpp|5|undefined reference to        `Conjunto<int>::Conjunto()'|
obj\Debug\src\Menu.o||In function `ZN4Menu5opcaoEv':|
C:\Trabalho04\src\Menu.cpp|22|undefined reference to     `Conjunto<int>::criaConjunto()'|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 1 second(s)) ===|

Set.

template <class T>
class Conjunto {
public:
    Conjunto();
    void criaConjunto();
    virtual ~Conjunto();

private:
    Conjunto<int> * conjunto;
    T * elementos;
    int qtd;
    int tam;
};

Set.cpp

#include "Conjunto.hpp"
#include "Bibliotecas.hpp"

template <class T>
Conjunto<T>::Conjunto() {
    elementos = new T[10];
    qtd = 0;
    tam = 10;
}

template <class T>
void Conjunto<T>::criaConjunto(){
    int num;
    cin >> num;
    if(conjunto!=NULL) {
        conjunto = NULL;
        delete conjunto;
    }
    if(num == 0) {
        conjunto = new Conjunto<int>();
    } else {
        conjunto = new Conjunto<int>(num);
    }
}

template <class T>
Conjunto<T>::~Conjunto() {
    delete [] elementos;
    elementos = NULL;
}

Menu.cpp

#include "Menu.hpp"

Menu::Menu() {
    conjunto = new Conjunto<int>();
}

void Menu::opcao() {    
    int opcao = 0;
    do {
        opcao = menu();
        switch(opcao) {
            case 1:
                conjunto->criaConjunto();
            break;
            case 2:
                //Sair
            break;            
        }
    } while(opcao != 2);
}

Menu.hpp

#include "Conjunto.hpp"

class Menu {
    public:
        Menu();
        int menu();
        void opcao();
        virtual ~Menu();

    //private:
        int tam;
        Conjunto<int> * conjunto;
};

1 answer

1


Your code itself is correct, the problem lies in the organization of files and the way the compiler works, especially with functions and template classes.

funcs.cpp:

template <typename T>
T f(const T& obj) { return obj; }

int g(int x) { return x; }

This file defines two functions, the g and the f<T>, but template functions cannot actually be compiled as long as the T is unknown. Seeing only this file there is no use of the function f, no value for T. The only thing that will be compiled here is g. Now the main:

main.cpp:

// isso estaria no seu header:
// ao incluir você **afirma** para o compilador que essas funções já
// existem em algum outro arquivo, ele confiará nessa afirmação.
template <typename T> T f(const T& obj);
int g(int x);

int main() {
    return f(5) + g(4); // f<int> é usado aqui, mas nesse arquivo não temos
                        // a definição de f<T>, então só dá para supor que
                        // f<int> vai estar em outro arquivo.
}

At the end, when compile the whole program, nobody will have compiled the function f<int>. funcs.cpp did not know it needed T=int, and main.cpp didn’t even have the definition to compile.

Two solutions:

  1. Tell functions.cpp that f<int> will be necessary. This is called Explicit template instantiation:

    // Dentro de funcs.cpp:
    template int f<int>(const int& obj); // força T=int a ser compilado.
    
  2. Cause the definition of f is available for main.cpp as well. This is the most common solution, which is to include the definition of f header, not source. It looks like this:

    funcs.cpp:

    #include <funcs.hpp>
    int g(int x) { return x; }
    

    funcs.hpp:

    template <typename T>
    inline T f(const T& obj) { return obj; } // Repare que o inline é necessário aqui
    

    main.cpp:

    #include <funcs.hpp>
    
    int main() {
        return f(5) + g(4); // f<int> é necessário aqui, e a definição completa
                            // foi incluida pelo funcs.hpp: será compilado
    }
    

All this also applies to functions within classes. The syntax for an Explicit instantiation template would be like this:

template Conjunto<int>::Conjunto();
template void Conjunto<int>::criaConjunto();
template Conjunto<float>::Conjunto();  // supondo que precise de float no futuro
template void Conjunto<float>::criaConjunto();

But even better is to include the definitions of all functions in the header of your class, always remembering to use the inline. So you don’t need to know what all kinds of T you need

  • I understood your answer but I’m not able to apply to my code, my error occurs here Menu::Menu() {&#xA; conjunto = new Conjunto<int>();&#xA;} when trying to allocate a pointer and msm occurs if trying to create a set class object. Could explain more or give another example involving classes?

  • Moved everything in the.cpp Set to the end of the.hpp Set and added inline in office?

  • Thus works perfectly, but would like to do in separate files .cpp and .hpp only for learning (this is the final work of data structure), but if it is too laborious or complicated otherwise this way is great. Thanks for the tips so far.

  • @Fabricioandrade, in this case, to keep separate, need to declare each function passing the types you will use at the end of yours . cpp, such as my last example in the answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.