6

I'm having trouble creating a class object from a template class in which I need the constructor to also be a template and accept a parameter when the object is created. However, when I attempt to create the object, I receive an error message stating that I'm referencing something that doesn't exist.

Here's my code:

using namespace std;
#include <cstdlib>

template <class Node_Type>
class BinaryTree 
{
public:
    BinaryTree(Node_Type);
    BinaryTree(Node_Type, Node_Type);
    BinaryTree(Node_Type, Node_Type, Node_Type);
    bool isEmpty();
    Node_Type info();
    Node_Type inOrder();
    Node_Type preOrder();
    Node_Type postOrder();


private:
    struct Tree_Node
{
    Node_Type Node_Info;
    BinaryTree<Node_Type> *left;
    BinaryTree<Node_Type> *right;
};

Tree_Node *root;

};

#endif

and my .cpp:

template <class Node_Type>
BinaryTree<Node_Type>::BinaryTree(Node_Type rootNode) {

    root = rootNode;
    root->left = NULL;
    root->right = NULL;

}

There's more to the .cpp, but it's just other function members that are irrelevant. My constructor shown above is what I can't get to work.

In my main, I'm attempting to declare my object with the call:

BinaryTree<char> node('a');

but when I try this, I get an error message stating:

undefined reference to `BinaryTree<char>::BinaryTree(char)'

I've been trying to figure this out for two days now. I've Googled every topic I can think of and read countless examples on Stack Overflow and other sources with no help. Can anyone please explain what my problem is? I know how to do my project, and I'd be finished by now if the syntax wasn't so ridiculous in C++. Thanks in advance!

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
Stephen Fish
  • 65
  • 1
  • 1
  • 4
  • 1
    The answer to your problem is *exactly* what the compiler is telling you. a `Tree_Node*` is *not* a `char`. – WhozCraig Sep 18 '13 at 19:38
  • 1
    Short and imprecise: Because you have the body of your function that depends on the template inside of the `.cpp` file. It needs to be with your `.h` file either directly within your class definition or if you don't like to place it there because of to keep it easieer to read, declare the function as `inline` and move it outside the class definition, either in the same file or into a `.hpp` which you include in the `.h` – t.niese Sep 18 '13 at 19:38
  • @WhozCraig is right `root` is of type `Tree_Node*` not `char` – loki Sep 18 '13 at 19:40
  • Ah... I'm such an idiot. I changed 'root = rootNode' to 'root->Node_Info = rootNode' and it works just fine now. Sometimes it just takes a second pair of eyes. Thanks, guys! – Stephen Fish Sep 18 '13 at 19:47
  • 1
    @StephenFish that isn't right either, but ok. Its your funeral. Your root pointer and your tree node left and right pointers are *all* uninitialized. – WhozCraig Sep 18 '13 at 19:48
  • possible duplicate of ["Undefined reference to" template class constructor](http://stackoverflow.com/questions/8752837/undefined-reference-to-template-class-constructor) – Alan Stokes Sep 18 '13 at 19:48
  • @WhozCraig for me `undefined reference to 'BinaryTree::BinaryTree(char)'` looks like a linking error. That what you said is true, but I would there expect an error like `Assigning to BinaryTree::TreeNode *' from incompatible type 'char'` (after fixing the linking error) or did I overlook something? – t.niese Sep 18 '13 at 19:52
  • 1
    @t.niese it certainly is. the error I saw was a completely different one (and oddly, the OP's compiler apparently is letting it through, whereas mine... is exhibiting exactly what you described. When he responded "I changed 'root = rootNode' to 'root->Node_Info = rootNode' and it works just fine now." I cringed. Step out of one undefined behavior, and smack into a different one. – WhozCraig Sep 18 '13 at 19:54
  • Possible duplicate of [this question](http://stackoverflow.com/questions/8752837/undefined-reference-to-template-class-constructor): [the answer](http://stackoverflow.com/a/8752879/1162533) is excellent. – vinit_ivar Sep 18 '13 at 19:42

3 Answers3

10

Template code should be visible at the time of instantiation, meaning that the definition of the functions must also be in the header.

stefaanv
  • 14,072
  • 2
  • 31
  • 53
6

Solution: You can't separate template implementations from the header file. Simply don't use a cpp file and put the definition in your header file (.h file).

Why? This is because cpp files can become precompiled sources, while templates are compile-time objects; therefore, the compiler cannot decide what type to use unless specified. So just put all your template undefined implementations in your header .h file.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • 1
    While this is a correct statement, is covered extensively [in this question](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file), and would ultimately be a problem regardless, it is *not* the answer to the OP's question concerning the specific error message being received. – WhozCraig Sep 18 '13 at 19:40
  • 1
    @WhozCraig Are you sure? The error message seems to be complaining that the constructor is not defined, not that the constructor is ill defined. – Alan Stokes Sep 18 '13 at 19:47
  • 1
    @AlanStokes Actually, its *both*, but you are correct, and I'll send Samer an upvote accordingly (in fact all three of them). The OP's code is going to have to morph extensively regardless before it is finally functional. Thanks for pointing it out. I misread the original error message. – WhozCraig Sep 18 '13 at 19:51
2

You can force the instantiation of the template in another cpp file.

BinaryTree<char>;
BinaryTree<int>;
BinaryTree<double>;

That way all the functions do not need to be in header files. Some people use the extension .inl for the files with the template implementations. So the .inl file is only needed when the instantiation doesn't already exist.

QuentinUK
  • 2,997
  • 21
  • 20