0

I was getting the following error from Visual Studio:

error LNK2019: unresolved external symbol "public: __thiscall BSTree::BSTreeNode::BSTreeNode(class TestData const &,class BSTree::BSTreeNode *,class BSTree::BSTreeNode *)" (??0BSTreeNode@?$BSTree@VTestData@@H@@QAE@ABVTestData@@PAV01@1@Z) referenced in function "protected: void __thiscall BSTree::insertHelper(class TestData const &,class BSTree::BSTreeNode * &)" (?insertHelper@?$BSTree@VTestData@@H@@IAEXABVTestData@@AAPAVBSTreeNode@1@@Z)

Here are the functions that the compiler brings into question:

template < typename DataType, class KeyType >
BSTree<DataType,KeyType>::BSTreeNode::BSTreeNode ( const DataType &nodeDataItem, 
BSTreeNode *leftPtr, BSTreeNode *rightPtr ) {
    left = leftPtr;
    right = rightPtr;
    dataItem = nodeDataItem;
}

template < typename DataType, class KeyType >
void BSTree<DataType,KeyType>::insertHelper (const DataType& d, BSTreeNode*& b) {
    if (b == 0) {
        b = new BSTreeNode(d, 0, 0);
    } 
    else if (d.getKey() < b->dataItem.getKey()) {
        insertHelper(d, b->left);
    }
    else if (d.getKey() > b->dataItem.getKey()) {
        insertHelper(d, b->right);
    }
    else {
        b->dataItem = d;
    }
}

So I look them over and I believe my code looks correct. So I perform a rebuild and then there are no errors and the program fires up just fine. I asked my lab instructor about why this occurs, but he said that he did not have a suitable answer. This is something that I've seen more and more while my projects are becoming more complex.

I've done some research:

Difference between build, rebuild, and clean solution

Do I always need to Clean/Rebuild?

Is this something that just inevitably happens while working with the Visual Studio IDE? Is there anything that I can do to prevent this? Are there any consequences from having this issue come up (ie. is there a flaw in my code)?

UPDATED: It seems that this might be the cause for much of the linker errors that I've been witnessing the semester.

Here is the advice from a book:

"Compiling programs that use templated classes requires a change in what files are included using the #include preprocessor directive, and in how the program is compiled. Because of how C++ compilers process templated code, the program that creates objects of the classes (e.g., main.cpp) must include the class implementation file, not the class declaration file. That is, it must do #include "Classname.cpp" instead of the usual #include "Classname.h" The rule is in effect the rest of this book. Because the main implementation file does a #include of the class implementation code, the class implementation code is not compiled separately."

Community
  • 1
  • 1
Derek W
  • 9,708
  • 5
  • 58
  • 67
  • Is the code in a separate source file or in a header file? Templates need to be fully defined in the header files, so all implementation needs to be in the header file and not in a separate source file. – Some programmer dude Nov 04 '12 at 19:37
  • I see. Our lab manual requests that the implementation is in a source file. When we use the template function in a main function we use #include "foo.cpp". It's ugly as the authors note, but it gets the job done. – Derek W Nov 04 '12 at 19:46
  • 1
    Wow. That is really bad advice, and the authors of your lab manual seem to know it. Member functions that are defined out-of-line (outside the class) but are qualified as inline need to be defined in a header. I see that your functions are not qualified as inline, but they should be. If multiple source files need to use that template and both `#include "foo.cpp"`, you'll have a violation of the one definition rule. Anytime you have to #include a source file in some other file you should think twice, and then keep on thinking until you finally think that this is a very bad idea. – David Hammen Nov 04 '12 at 20:09
  • @DavidHammen: I added the excerpt of my books advice. – Derek W Nov 04 '12 at 20:24
  • To clarify what David is saying a little. You should put the functions in the header file, and they need to be declared as inline. – CrazyCasta Nov 04 '12 at 20:28
  • Is it likely that the reason for my constant issues of linker errors to be resolved by a rebuild is caused by this approach. – Derek W Nov 04 '12 at 20:31
  • @CrazyCasta - template functions **do not** have to be declared inline, even though they are in a header file. The implementation is responsible for eliminating duplicates. – Pete Becker Nov 04 '12 at 20:41
  • @DavidHammen - I agree with most of your answer, but template function definitions **do not** have to be declared inline. There is no ODR violation so long as the definitions in different source files are the same. – Pete Becker Nov 04 '12 at 20:43
  • 1
    @DerekW - this approach is probably not the cause of unresolved symbol errors. If anything, it would cause duplicated symbol errors if the .cpp file with the template definitions also defines a non-template function or two. That aside, while the extension .cpp defies conventions, #including one is no different from #including a file with any other extension. What's legal for a .h is legal for .cpp and for any other extension. The compiler doesn't care. – Pete Becker Nov 04 '12 at 20:47
  • @PeteBecker - re *There is no ODR violation so long as the definitions in different source files are the same* -- That's wrong. From the standard, 3.2 para 3: "Every program shall contain exactly one definition of every non-inline function or object that is used in that program; **no diagnostic required**." Just because you don't get a complaint from some toolchain doesn't mean you aren't violating the ODR. The toolchain doesn't have to tell you about ODR violations because no diagnostic is required. You **are** violating the ODR, and another toolchain is free to reject your code. – David Hammen Nov 04 '12 at 21:27
  • @PeteBecker - re *The compiler doesn't care.* It's not just the compiler; it's the entire toolchain here that is deciding what needs to be recompiled. Those file suffixes don't have much meaning in Linux, but they do have significant meaning in Windows (and to a lesser extent, MacOS as well). That `#include "foo.cpp"` might well be the cause of the problem. – David Hammen Nov 04 '12 at 21:44
  • @DavidHammen - a template is neither a non-inline function nor an object. The ODR does not apply to duplicate definitions of a template, just as it doesn't apply to duplicate definitions of a class. – Pete Becker Nov 04 '12 at 22:14
  • @DavidHammen - I suppose it's possible that an automated build system would look at a source file that uses `#include "foo.cpp"` and determine that it doesn't need to recompile that source file just because "foo.cpp" changed, but that seems unlikely. – Pete Becker Nov 04 '12 at 22:19
  • As a programmer of many years I am just in the habit of pressing clean any time I see anything strange. If the project is small I might do this twice a day. We should never _have_ to press clean. (Except for backup maybe) Also, pre-compiled headers give me the sort of problem you are describing. Some people have no problems with them. The easy way to eliminate that problem is to turn pre-compiled headers off. The harder way, and necessary if you have a large project, is to understand exactly what the pre-compiled header is doing for you. – Ant Nov 14 '12 at 17:33

0 Answers0