Link errors with my Doubly Linked List Class Template?

I keep getting Link errors when trying to run my code. I have 4 LNK2019 Errors saying that there is "unresolved external symbol". So I assume at run-time the compiler cannot find my function definitions?

*UPDATE* I just did some more research and it appears that placing the function template definitions in the .h file fixes this problem. I just tried this in Visual Studio and can confirm that it fixed my issue. Is there any easy way around this issue so that I can have a .h file and .cpp file separately for a class template declaration and its function template implementations?

Here is the main .cpp file that I use to drive my program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
#include "ListClass.h"
#include "DLLTemplate.h"
using namespace std;

int main()
{
	ListClass list;
	DLLTemplate<double> TemplateList;
	char choice;
	double value, position;
	string text, whichText;

	TemplateList.addAtBeginning(5.0);
	TemplateList.addInMiddle(2.0, 5.0);
	TemplateList.addAtEnd(19.0);
	TemplateList.addAtEnd(21.0);
	TemplateList.PrintForward();
        
        return 0;
}


DLLTemplate.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <string>
using namespace std;

template <class T>
#ifndef DLLTEMPLATE_H
#define DLLTEMPLATE_H

class DLLTemplate
{
private:
	struct ListNode
	{
		T value;
		struct ListNode *next;
		struct ListNode *prev;
	};

	ListNode *head;
	ListNode *tail;

public:
	DLLTemplate() //Constructor
	{
		head = nullptr;
		tail = nullptr;
		//cout << "hello" << endl;
	}

	~DLLTemplate()
	{
		ListNode *NodePtr;
		ListNode *nextNode;
		NodePtr = head;

		while (NodePtr != nullptr)
		{
			nextNode = NodePtr->next;
			cout << " deleting ... " << NodePtr->value << endl;
			delete NodePtr;
			NodePtr = nextNode;
		}
	}

	//doubly linked list operations
	void addAtEnd(T);
	void addAtBeginning(T);
	void addInMiddle(T, T);
	bool checkPosition(T);
	void DeleteFirst();
	void DeleteLast();
	void DeleteValue(T);
	void PrintForward() const;
	void PrintReverse() const;
};

#endif 


Finally, the DLLTemplate.cpp with the function implementations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#include "DLLTemplate.h"
#include <iostream>
#include <string>
using namespace std;

template <class T>
void DLLTemplate<T>::addAtEnd(T val)
{
	ListNode *newNode; //points at a new Node
	ListNode *NodePtr; // used to traverse the list

	newNode = new ListNode;
	newNode->value = val;
	newNode->next = nullptr;
	newNode->prev = tail;

	if (head == nullptr)
	{
		head = newNode;
		tail = newNode;
	}
	else
	{
		NodePtr = head;

		while (NodePtr->next != nullptr)
			NodePtr = NodePtr->next;

		NodePtr->next = newNode;
		tail->next = newNode;
		tail = newNode;
	}
}

template <class T>
void DLLTemplate<T>::addAtBeginning(T val)
{
	ListNode *newNode;

	newNode = new ListNode;
	newNode->value = val;
	newNode->next = head;
	newNode->prev = nullptr;

	if (head == nullptr)
	{
		head = newNode;
		tail = newNode;
	}
	else
	{
		head->prev = newNode;
		head = newNode;
	}
}

template <class T>
void DLLTemplate<T>::addInMiddle(T val, T position)
{
	ListNode *newNode;
	ListNode *NodePtr;

	newNode = head;

	newNode = new ListNode;
	newNode->value = val;
	newNode->next = nullptr;

	if (head == nullptr)
	{
		head = newNode;
		tail = newNode;
	}
	else if (tail->value == position)
	{
		NodePtr = head;
		newNode->prev = tail;

		while (NodePtr->next != nullptr)
		{
			NodePtr = NodePtr->next;
		}

		NodePtr->next = newNode;
		tail->next = newNode;
		tail = newNode;
	}
	else
	{
		NodePtr = head;

		while (NodePtr->next != nullptr && NodePtr->value != position)
		{
			NodePtr = NodePtr->next;
		}

		newNode->next = NodePtr->next;
		NodePtr->next = newNode;
		newNode->prev = NodePtr;
		NodePtr = NodePtr->next->next;
		NodePtr->prev = newNode;

	}
}

template <class T>
bool DLLTemplate<T>::checkPosition(T val)
{
	ListNode *NodePtr;
	NodePtr = head;
	bool found = false;

	while (NodePtr != nullptr)
	{
		if (NodePtr->value == val)
		{
			found = true;
			break;
		}
		NodePtr = NodePtr->next;
	}

	return found;
}

template <class T>
void DLLTemplate<T>::DeleteFirst()
{
	ListNode *NodePtr;

	if (head != nullptr)
	{
		NodePtr = head->next;
		delete head;
		head = NodePtr;
		head->prev = nullptr;
	}
	else
		cout << "list is empty" << endl;
}

template <class T>
void DLLTemplate<T>::DeleteLast()
{
	ListNode *NodePtr;
	if (tail != nullptr)
	{
		NodePtr = tail->prev;
		delete tail;
		tail = NodePtr;
		tail->next = nullptr;
	}
	else
		cout << "list is empty" << endl;
}

template <class T>
void DLLTemplate<T>::DeleteValue(T val)
{

	bool found = false;
	found = checkPosition(val);

	if (found == true)
	{
		ListNode *NodePtr;
		ListNode *prevPtr = nullptr;

		if (head != nullptr)
		{
			if (head->value == val)
			{
				NodePtr = head->next;
				delete head;
				head = NodePtr;
				head->prev = nullptr;
			}
			else if (tail->value == val)
			{
				NodePtr = tail->prev;
				delete tail;
				tail = NodePtr;
				tail->next = nullptr;
			}
			else
			{
				NodePtr = head;

				while (NodePtr != nullptr && NodePtr->value != val)
				{
					prevPtr = NodePtr;
					NodePtr = NodePtr->next;
				}
				if (NodePtr != nullptr)
				{
					prevPtr->next = NodePtr->next;
					delete NodePtr;
					NodePtr = prevPtr->next;
					NodePtr->prev = prevPtr;
				}
			}
		}
	}
	else if (found == false && head == nullptr)
		cout << "list is empty" << endl;
	else if (found == false && head != nullptr)
		cout << "The value: " << val << " was not found in this list..." << endl;
}

template <class T>
void DLLTemplate<T>::PrintForward() const
{
	ListNode *NodePtr;

	NodePtr = head;
	cout << "Printing Forward..." << endl;
	while (NodePtr != nullptr)
	{
		cout << "value: " << NodePtr->value << endl;
		NodePtr = NodePtr->next;
	}
	cout << "-------------" << endl;
	cout << "Head: " << head->value << endl;
	cout << "Tail: " << tail->value << endl;
	cout << endl;
}

template <class T>
void DLLTemplate<T>::PrintReverse() const
{
	ListNode *NodePtr;

	NodePtr = tail;
	cout << "Printing In Reverse..." << endl;
	while (NodePtr != nullptr)
	{
		cout << "value: " << NodePtr->value << endl;
		NodePtr = NodePtr->prev;
	}
	cout << "-------------" << endl;
	cout << "Head: " << head->value << endl;
	cout << "Tail: " << tail->value << endl;
	cout << endl;
}


So the question is, how can I fix these link errors? I was under the impression that everything in the code is correct.
]
Last edited on
When I use the a non class template object like the list that I created, I don't have any problems. Only when using a class template do I get the link errors.
Example... this code below will compile just fine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
#include "ListClass.h"
#include "DLLTemplate.h"
using namespace std;

int main()
{
	ListClass list;
	DLLTemplate<double> TemplateList;
	char choice;
	double value, position;
	string text, whichText;

	list.addAtBeginning(5.0);
	list.addInMiddle(2.0, 5.0);
	list.addAtEnd(19.0);
	list.addAtEnd(21.0);
	list.PrintForward();

        return 0;
}
Last edited on
Is there any easy way around this issue so that I can have a .h file and .cpp file separately for a class template declaration and its function template implementations?

The compiler has to know the implementation code at compile time so it can instantiate the template. To separate the declaration from the code, put the code in a DLLTemplate.i and add #include "DLLTemplate.i" at the bottom of DLLTemplate.h.
> Is there any easy way around this issue so that I can have a .h file and .cpp file separately
> for a class template declaration and its function template implementations?

Yes, provided that you know before hand all the template arguments for which the template needs to be instantiated in the program.

An explicit instantiation declaration (an extern template) skips implicit instantiation step: the code that would otherwise cause an implicit instantiation instead uses the explicit instantiation definition provided elsewhere (resulting in link errors if no such instantiation exists). This can be used to reduce compilation times by explicitly declaring a template instantiation in all but one of the source files using it, and explicitly defining it in the remaining file.

http://en.cppreference.com/w/cpp/language/class_template
As @dhayden said you can put the code in a #include "DLLTemplate.i" and include it at the buttom of the .h file or you could do it as a .cpp that it included at the end of a .h although both should generally be avoided (perfectly you should never include a .cpp in a .h file), but as an example:

templated_code.h:
--------------------
1
2
3
4
5
6
7
8
#ifndef TEMPLATED_CODE_H
#define TEMPLATED_CODE_H

//your class declaration with its template goes here

#include "templated_code.cpp"

#endif 


templated_code.cpp:
----------------------
1
2
3
#include templated_code.h

//implementation goes here 


OR you have another option:
in your .cpp file simply add at the end of the file:
template class DLLTemplate<double>;
however it will only work when you make a double list, if you would want to use an int list you should add:
template class DLLTemplate<int>;
to the end of the file (just change to the type you want to use insode of the <>).
Topic archived. No new replies allowed.