Error undefined reference when trying to use a template class

1

I have a Set template class and a Menu class both with their respective .cpp and .hpp, when trying to use a pointer of the set class 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.hpp

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;
};
    
asked by anonymous 04.12.2015 / 22:20

1 answer

1

Your code itself is correct, the problem is with the file organization and how 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, g and f<T> , but template functions can not really be compiled while the T is unknown. Seeing only this file there is no use of the f function, 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.
}

In the end, when compiling the whole program, nobody compiled the f<int> function. funcs.cpp did not know that it needed T=int , and main.cpp nor had the definition to compile.

Two solutions:

  • Say to funcs.cpp that f<int> will be required. This is called explicit template instantiation :

    // Dentro de funcs.cpp:
    template int f<int>(const int& obj); // força T=int a ser compilado.
    
  • Make the definition of f available to main.cpp as well. This is the most common solution, which is to include the definition of f in the header, not the 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 template instantiation would look 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 it's even better to include the definitions of all the functions in the header of your class, always remembering to use inline . So you do not need to know what all the types of T you need

        
    06.12.2015 / 13:47