Question:
Here is the test source
#include <common.hpp>
#include <stdio.h>
using namespace RFw;
int main() {
Array<int> _a_;
_a_.resize(5);
_a_[2] = 'b';
_a_[3] = 'a';
printf("%c%c\n", *(_a_.provideElement(2)), *(_a_.provideElement(3)));
return 0;
}
So I compile:
c++ test.cpp -o test -std=c++11 -Isrc-core/ -Lbin/ -lcore
Conclusion:
/tmp/test-k6aFM8.o: In function `main':
test.cpp.text+0x27): undefined reference to `RFw::Array<int>::Array(unsigned int, int*)'
test.cpp.text+0x3a): undefined reference to `RFw::Array<int>::resize(unsigned int)'
test.cpp:(.text+0x52): undefined reference to `RFw::Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x76): undefined reference to `RFw::Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x9a): undefined reference to `RFw::Array<int>::provideElement(unsigned int)'
test.cpp:(.text+0xbd): undefined reference to `RFw::Array<int>::provideElement(unsigned int)'
test.cpp:(.text+0x101): undefined reference to `RFw::Array<int>::~Array()'
test.cpp:(.text+0x11b): undefined reference to `RFw::Array<int>::~Array()'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
PS Array is taken from Array.hpp which is included in common.hpp
array.hpp:
/**
* src-core/Array.hpp
*/
#ifndef _ARRAY_HPP_
#define _ARRAY_HPP_
#include "util.hpp"
#include "Object.hpp"
#include "arrayExceptions.hpp"
namespace RFw {
template<typename TValue>
class Array : public Object {
public:
Array(intnum _length = 0, TValue* _array = nullptr);
virtual ~Array();
void addToTop(const TValue _element) throw(ArrayOverflowException);
void addToEnd(const TValue _element) throw(ArrayOverflowException);
void clone(const intnum _length, const TValue* _array);
TValue* provideElement(const intnum _index) throw(IndexOutOfRangeException);
const TValue* provideElement(const intnum _index) const throw(IndexOutOfRangeException);
TValue& operator[](const intnum _index) throw(IndexOutOfRangeException);
const TValue& operator[](const intnum _index) const throw(IndexOutOfRangeException);
void resize(const intnum _newLentgh);
const Array<TValue> getClone() const;
const intnum getLength() const;
const TValue* getArray() const;
private:
intnum length__;
TValue* array__;
};
}
#endif // _ARRAY_HPP_
Answer:
With templates, you must put the implementation of the methods in the header.
In your case, write like this:
template<typename TValue>
class Array : public Object {
public:
Array(intnum _length = 0, TValue* _array = nullptr) :
length__(_length),
array__(new TValue[_length]) {
// остаток имплементации
}
virtual ~Array() {
delete[] array__;
}
// и так далее
};
If you put the implementation of the methods in a .cpp file, the compiler won't know which template instantiations it needs to compile. This is a limitation of the C++ compilation model . Sorry for this.
A little explanation. In C++, a template is not a class, it's just a "description" by which a real class is generated when template arguments are substituted. The template itself does not produce any object code.
When the compiler compiles a C++ file with code that uses a template, it sees what template argument the template is being used with. But when it compiles a C++ file with implementation of template methods, it doesn't know with what arguments the template was instantiated in other files, because each C++ file is compiled separately!
So the solution is to put the implementation inside the template class . At the same time, the compiler, at the point where it sees the instantiation of the template, sees all its code, and can compile all the methods of the class.
(There is also an ugly crutch with specifying in the C ++ file the exact list of template instantiations, which is so terrible that I don’t want to talk about it. An analogue to this would be to indicate in advance, next to each function, a list of all arguments with which the function will be called in the program. )