link errors with template code [duplicate] - c++

This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Why do I get "unresolved external symbol" errors when using templates? [duplicate]
(3 answers)
Closed 9 years ago.
I have 3 files - main, Array.hh, and Array.cc. When I do "g++ main.cc Array.cc", I get all kinds of linker errors, e.g.: "undefined reference to Array<<\double>>::Array(int)"
I read the other stackoverflow entries for linker errors with templates, but they are recommending to split the HH and CC files, which I have already done. So what can be the problem here?
Edit: Thanks for the replies. I had not understood the posts I had read previously. Merging the Array.cc into the Array.hh solves the problem. Closed.
main.cc:
#include "Array.hh"
#include <iostream>
int main() {
Array<int> anArray(12);
Array<double> adArray(12);
for (int nCount = 0; nCount < 12; nCount++) {
anArray[nCount] = nCount;
adArray[nCount] = nCount + 0.5;
}
for (int nCount = 11; nCount >= 0; nCount--)
std::cout << anArray[nCount] << "\t" << adArray[nCount]
<< std::endl;
return 0;
}
Array.hh:
template <typename T>
class Array {
private:
int m_nLength;
T *m_ptData;
public:
Array();
Array(int nLength);
~Array();
void Erase();
T& operator[](int nIndex);
int GetLength();
};
Array.cc
#include "Array.hh"
template <typename T>
Array<T>::Array() {
m_nLength = 0;
m_ptData = 0;
}
template <typename T>
Array<T>::Array(int nLength) {
m_ptData= new T[nLength];
m_nLength = nLength;
}
template <typename T>
Array<T>::~Array() { delete[] m_ptData; }
template <typename T>
void Array<T>::Erase() {
delete[] m_ptData;
m_ptData= 0;
m_nLength = 0;
}
template <typename T>
int Array<T>::GetLength() { return m_nLength; }

Put the definitions from Array.cc into Array.hh
When a template class is used, a class is created. Array.cc does not know which classes should be created from the template, only main.cc knows that. main.cc does not know how to create the classes, because it does not know the definition of the member functions of Array, only Array.cc knows that.

You need to #include "Array.cc"
A template can't be instantiated if its definition is not available.
This separation of definition and declaration is fine, but you must include the cc file whereever you are instatiating that template with a new type

Recommendations you took are not very accurate. You should not separate your templates into .h and .cpp files.
If you insist on putting them separate, "export" is the keyword you are looking for.
For more information about "export" check here: http://www.parashift.com/c++-faq/separate-template-fn-defn-from-decl-export-keyword.html.

Related

Undefined reference to struct method [duplicate]

This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 3 years ago.
I tried to make an Array template class but when I try to build the compiler fails to link the constructor and the method and I get :
undefined reference to `Array::Array()
undefined reference to `Array::getSize()
Here is the header file:
#pragma once
template<typename type, int length>
struct Array{
public:
Array();
int getSize();
private:
type data[length];
int m_length;
};
The Array.cpp file:
#include "Array.h"
template<typename t, int l>
Array<t, l>::Array()
{
m_length = l;
}
template<typename type, int length>
Array<type, length>::getSize()
{
return m_length;
}
And the main function:
#define LOG(x) cout<<x<<endl
int main()
{
Array<int, 10> array;
LOG(array.getSize());
}
If someone has any idea about why I am getting this, I would really appreciate.
You need to either put your implementation into the header files, or define the usage (instantiation of the template arguments) in the source file

Undefined reference error in C++ Link step [duplicate]

This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 8 years ago.
I'm trying to make a simple bounds checked array in C++. I have declared a class in a header file, and defined the class in a separate source file. The compile step runs fine, but when I try to link the objects, I get the following error:
$ g++.exe -o a.exe src\main.o src\array.o
src\main.o: In function `main':
../src/main.cpp:7: undefined reference to `Array<int>::Array(int)'
../src/main.cpp:9: undefined reference to `Array<int>::operator[](int)'
../src/main.cpp:10: undefined reference t o `Array<int>::operator[](int)'
../src/main.cpp:11: undefined reference t o `Array<int>::operator[](int)'
../src/main.cpp:13: undefined reference t o `Array<int>::operator[](int)'
../src/main.cpp:7: undefined reference to `Array<int>::~Array()'
../src/main.cpp:7: undefined reference to `Array<int>::~Array()'
collect2.exe: error: ld returned 1 exit status
main.cpp
#include <iostream>
#include "array.hpp"
using namespace std;
int main() {
Array<int> x(10); // compiler error
x[0] = 1; // compiler error
x[1] = 2; // compiler error
x[2] = 3; // compiler error
cout << x[1] << endl; // compiler error
return 0;
}
array.hpp
#ifndef ARRAY_HPP_
#define ARRAY_HPP_
template <class T>
class Array{
private:
T* array;
int length_;
public:
Array(int);
~Array();
int Length();
int& operator[](int);
};
#endif /* ARRAY_HPP_ */
array.cpp
#include "array.hpp"
template <class T>
Array<T>::Array(int size) {
length_ = size;
array = new T[size];
}
template <class T>
Array<T>::~Array() {
delete[] array;
}
template <class T>
int Array<T>::Length() {
return length_;
}
template <class T>
int& Array<T>::operator[](const int index) {
if (index < 0 || index >= length_) {
throw 100;
}
return array[index];
}
Definitions of members of a template class shall be in the same header where the template class is defined itself.
Template classes must have all their code in the header file or they can only be used for types you explicitly instantiated in the cpp file.

using template declared in other file in c++ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why can templates only be implemented in the header file?
What is an undefined reference/unresolved external symbol error and how do I fix it?
I have defined a template class in a file.
point.h is
#ifndef POINT_H
#define POINT_H
using namespace std;
template <typename T, int size>
class point {
private:
T coordinates[size];
public:
point();
void set_coordinates(const T *);
void get_coordinates(T *);
};
#endif /* POINT_H */
point.c is
#include "point.h"
template <typename T, int size>
point::point() {
for (int i = 0; i < size; i++)
coordinates[i] = 0;
}
template <typename T, int size>
void point<T, size>::set_coordinates(const T *coordinates) {
for (int i = 0; i < size; i++)
this->coordinates[i] = coordinates[i];
}
template <typename T, int size>
void point<T, size>::get_coordinates(T *coordinates) {
for (int i = 0; i < size; i++)
coordinates[i] = this->coordinates[i];
}
I am using this template as point<int, 2> p0;. But compiler gives error that point<int, 2> is not defined.
I searched on this and found two solutions -
1. to use export variable. But I read that it is not supported by all compilers. So, I don't want to use that.
2. to create explicit class specializations like
template <> class point<int> {
...
}
But isn't there any other way to do this? (I mean in C++ standard containers, they might have used some technique to do this.)
Read this and the next two FAQ questions - C++ FAQ
The solution of the c++ standard containers is keep everything in the header file.
You should put all the definitions belonging to the template point (that is, including the member functions) to the point.h file and include it in any file that uses the point class, so that the compiler can instantiate it as needed. See this question for details.
The C++ compilers and linkers have means to avoid "multiple definitions" error on link (the ODR rule in the standard states this must be the case).

C++: Templates not working from another class

When the following project is compiled, I get the following compiler error: (Visual Studio 2010)
1>usingclass.obj : error LNK2019: unresolved external symbol "public: static int __cdecl c1::arrSize(int * const)" (??$arrSize#H#c1##SAHQAH#Z) referenced in function "public: void __thiscall usingclass::a(void)" (?a#usingclass##QAEXXZ)
Code:
Headers:
c1.h
#pragma once
#include <array>
class c1
{
c1(void);
~c1(void);
public:
template<class T>
static int arrSize(T arr[]);
};
usingclass.h
#pragma once
#include "c1.h"
class usingclass
{
public:
usingclass(void);
~usingclass(void);
void a();
};
Source files:
c1.cpp
#include "c1.h"
c1::c1(void)
{
}
c1::~c1(void)
{
}
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
usingclass.cpp
#include "usingclass.h"
usingclass::usingclass(void)
{
}
usingclass::~usingclass(void)
{
}
void usingclass::a()
{
int a[2] = {1,2};
int b = c1::arrSize<int>(a);
}
How do I fix that?
Don't do this! The declaration is misleading.
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
is equivalent to
template <class T>
int c1::arrSize(T *arr)
{
return (sizeof(arr)/sizeof(arr[0]));
}
which will not give you want you want. The proper way to do it is like this:
class c1
{
c1(void);
~c1(void);
public:
template<class T,int N>
static int arrSize(T (&arr)[N]) { return N; }
};
arrSize takes a reference to an array as a parameter. The type of the array and the size of the array are template parameters and can be deduced by the compiler. The inline member function then just returns the size determined by the compiler.
You need to move
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
inside c1.h.
Template implementations must be visible to all translation units using that template (unless it's specialized, and in your case it's not).
This solves the compiler error but the underlying issue is solved with Vaughn Cato's answer. I missed that. You'll still need the definition in the header.
I think you have to define your template in c1.h itself. Because when you are including c1.h in your usingclass.h, and try to use template it does not find the expansion for template.
Or If you want to go with implementation of template in c1.cpp, then you have to include c1.cpp as well in usingclass.h.

Error in creating template class

I found this vector template class implementation, but it doesn't compile on XCode.
Header file:
// File: myvector.h
#ifndef _myvector_h
#define _myvector_h
template <typename ElemType>
class MyVector
{
public:
MyVector();
~MyVector();
int size();
void add(ElemType s);
ElemType getAt(int index);
private:
ElemType *arr;
int numUsed, numAllocated;
void doubleCapacity();
};
#include "myvector.cpp"
#endif
Implementation file:
// File: myvector.cpp
#include <iostream>
#include "myvector.h"
template <typename ElemType>
MyVector<ElemType>::MyVector()
{
arr = new ElemType[2];
numAllocated = 2;
numUsed = 0;
}
template <typename ElemType>
MyVector<ElemType>::~MyVector()
{
delete[] arr;
}
template <typename ElemType>
int MyVector<ElemType>::size()
{
return numUsed;
}
template <typename ElemType>
ElemType MyVector<ElemType>::getAt(int index)
{
if (index < 0 || index >= size()) {
std::cerr << "Out of Bounds";
abort();
}
return arr[index];
}
template <typename ElemType>
void MyVector<ElemType>::add(ElemType s)
{
if (numUsed == numAllocated)
doubleCapacity();
arr[numUsed++] = s;
}
template <typename ElemType>
void MyVector<ElemType>::doubleCapacity()
{
ElemType *bigger = new ElemType[numAllocated*2];
for (int i = 0; i < numUsed; i++)
bigger[i] = arr[i];
delete[] arr;
arr = bigger;
numAllocated*= 2;
}
If I try to compile as is, I get the following error:
"Redefinition of 'MyVector::MyVector()'"
The same error is displayed for every member function (.cpp file).
In order to fix this, I removed the '#include "myvector.h"' on the .cpp file, but now I get a new error:
"Expected constructor, destructor, or type conversion before '<' token".
A similar error is displayed for every member as well.
Interestingly enough, if I move all the .cpp code to the header file, it compiles fine. Does that mean I can't implement template classes in separate files?
It's always a good idea to place your templates in a header file. That way you don't mess up the linker with multiple definitions of the same instantiations and such.
And of course there's the circular inclusion :).
First, you have
#include "myvector.cpp"
which creates a circular reference between the files. Just get rid of it.
The other problem is that you are defining a template class inside a .cpp file. Template definitions are only allowed inside header files. There may be ways around that, but for g++ (which XCode uses) that's how the cookie crumbles.