Templated class inheritance

Hi guys,

I've just created a main frame for a statistical tool in C++.
Following is my code:

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
//StatTool.h
#ifndef STATTOOL_H
#define STATTOOL_H


class StatTool{
	public:
		StatTool();
		virtual ~StatTool();
	
	private:
		double Mean;
		double Error;
};

#endif /*STATTOOL_H*/

//StatTool.cpp

#include "../include/StatTool.h"


StatTool::StatTool()
	 :Mean(0), Error(0){}
StatTool::~StatTool(){
	std::cout << "Destroyed StatObj" <<std::endl;
}



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
//StatTool.h
#include "StatTool.h"

#ifndef STATTOOL1D_H
#define STATTOOL1D_H

template <class T>
class StatTool1D: public StatTool{
	public:
		StatTool1D(T _tArr[], int _tArrSizeX);
		~StatTool1D();
		double getMean();
		double getError();
	
	private:
		void calcMean();
		void calcError();
		T *tArr;
		int tArrSizeX;
};

#endif /*STATTOOL1D_H*/


//StatTool.cpp
#include "../include/StatTool.h"
#include "../include/StatTool1D.h"

template <class T>
StatTool1D<T>::StatTool1D(T _tArr[], int _tArrSizeX)
	      :StatTool(), tArr(_tArr), tArrSizeX(_tArrSizeX){
	std::cout << "Initialized Array" <<std::endl;
	std::cout << "Size of: " <<sizeof(tArr)<< " Size: " <<tArrSizeX<<std::endl;
}
template <class T>
StatTool1D<T>::~StatTool1D(){
	std::cout << "Destroyed Array" <<std::endl;
	//delete tArr;
}

template <class T>
double StatTool1D<T>::getMean(){
	calcMean();
	return Mean;
}
template <class T>
double StatTool1D<T>::getError(){
	calcError();
	return Error;
}

template <class T>
void StatTool1D<T>::calcMean(){
//process
}
template <class T>
void StatTool1D<T>::calcError(){
//process
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//stattest.cpp
#include "./include/StatTool.h"
#include "./include/StatTool1D.h"

int main(){
	int SIZE=5000;
	int intarr[SIZE];
	double doubarr[SIZE];
	
	for(int i=0; i<SIZE; i++){
		intarr[i]=i;
		doubarr[i]=i;
	}
	
	StatTool1D<int> StatInt(intarr, SIZE);
	StatTool1D<double> StatDoub(doubarr, SIZE);
	
	std::cout << "Mean int: " <<StatInt.getMean()<<std::endl;
	std::cout << "Error int: " <<StatInt.getError()<<std::endl;
	std::cout << "Mean double: " <<StatDoub.getMean()<<std::endl;
	std::cout << "Error double: " <<StatDoub.getError()<<std::endl;
	
	return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Makefile

CC=g++
CFLAGS=-O2 -Wall
LDFLAGS=-lm

all : stattest

StatTool.o : ./src/StatTool.cpp ./include/StatTool.h
	$(CC) $(CFLAGS) -c ./src/StatTool.cpp

StatTool1D.o : ./src/StatTool1D.cpp ./include/StatTool1D.h ./include/StatTool.h 
	$(CC) $(CFLAGS) -c ./src/StatTool1D.cpp

stattest.o : stattest.cpp ./include/StatTool.h ./include/StatTool1D.h
	$(CC) $(CFLAGS) -c stattest.cpp ./include/StatTool.h ./include/StatTool1D.h

stattest : stattest.o StatTool.o StatTool1D.o
	$(CC) $(CFLAGS) stattest.o StatTool.o StatTool1D.o ${LDFLAGS} -o stattest
	rm -rf *o
	
clean:
	rm -rf *.o stattest


When compiling I get undefined reference errors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ make      
g++ -O2 -Wall -c stattest.cpp ./include/StatTool.h ./include/StatTool1D.h
g++ -O2 -Wall -c ./src/StatTool.cpp
g++ -O2 -Wall -c ./src/StatTool1D.cpp
g++ -O2 -Wall stattest.o StatTool.o StatTool1D.o -lm -o stattest
stattest.o: In function `main':
stattest.cpp:(.text.startup+0x52): undefined reference to `StatTool1D<int>::StatTool1D(int*, int)'
stattest.cpp:(.text.startup+0x63): undefined reference to `StatTool1D<double>::StatTool1D(double*, int)'
stattest.cpp:(.text.startup+0x6c): undefined reference to `StatTool1D<int>::getMean()'
stattest.cpp:(.text.startup+0x9e): undefined reference to `StatTool1D<int>::getError()'
stattest.cpp:(.text.startup+0xd0): undefined reference to `StatTool1D<double>::getMean()'
stattest.cpp:(.text.startup+0x102): undefined reference to `StatTool1D<double>::getError()'
stattest.cpp:(.text.startup+0x134): undefined reference to `StatTool1D<double>::~StatTool1D()'
stattest.cpp:(.text.startup+0x13d): undefined reference to `StatTool1D<int>::~StatTool1D()'
stattest.cpp:(.text.startup+0x159): undefined reference to `StatTool1D<double>::~StatTool1D()'
stattest.cpp:(.text.startup+0x162): undefined reference to `StatTool1D<int>::~StatTool1D()'
collect2: ld returned 1 exit status
make: *** [stattest] Error 1 



I'm lost I've tried next to everything I could find via google and different forums.
I suspect it's fault lies in the inheritance, but I don't know where.

Thanks in advance. =)
As StatTool1D<> is a templated class, the implemention must also be in the header.

i.e. move the contents of stattest1d.cpp into stattest1d.h and ditch the .cpp file.

(the compiler needs to see the whole definition of a templated class at the point of instantiation, not just the declaration.)

Andy

PS also note you're using a C99-ism -- non-const array sizes -- which is not ANSI compliant for C++03 or C++11. GCC les you gt away it when you're not compiling in ANSI mode, but if you want to be cross-platform you need to eliminate this.

Give your current code, you could just make line 6 of stattest.cpp

const int SIZE=5000;

PPS Also, out of interest, where is std::cout coming from? I see no sign of <iostream> ??
Last edited on
Thank you very much Andy. I wasn't aware that splitting a templated class doesn't work.
But after thinking about it, it does make sense.

I hope my train of thought is right.
Is it true that the compiler can't link StatTool1D.h and StatTool1D.cpp because the data type is yet to be defined?
So the compiler doesn't know how much memory to reserve?
Careful with you terminolgy: you're never going to link a .h or .cpp file.

It's not about the reservation of memory as such, but it does have to know the memory layout of the class. Which is how much memory it would have to allocate for an instance of a class. And about types (in you case, just the one: T)

When the compiler parses a template definition, it stores the definition away somewhere. Then when you instantiate it (when you use it in stattest.cpp), it has to create (or generate) the actual implementations (in your case, a StatTool1D of ints and a StatTool1D of double.) As far as the object code goes, you might as well have written two version of StatTool1D -- StatTool1D_Int and StatTool1D_Doub. Except programmers are "lazy", so we use templating!

To generate the int and double versions, it must know all about the classes. Partly because it must know the memory layout of the class, as I've already said (though, as it happens, you're using a pointer for you data storage, so the generated types will have the same memory layout and size.) But also because the compiler must know about the template's types (T, etc) to generate the methods.

It looks like the compiler might have been able to get away without knowing the method definitions up front, given how little you do in them. But as the compiler is not prescient, so it has to allow for the fact that you might have used variables of type T in your methods.

Andy

PS Actually, templating it is less prone to bugs than cut and paste, too.
Last edited on
Topic archived. No new replies allowed.