This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 9 years ago.
I am getting this error when compiling it with: g++ main.cpp Vec.cpp -Wall -o main -I.
/tmp/cciqbEQJ.o: In function `main':
main.cpp:(.text+0x8b): undefined reference to `Vec<double>::Vec()'
main.cpp:(.text+0x9b): undefined reference to `Vec<double>::~Vec()'
collect2: ld returned 1 exit status
make: *** [all] Error 1
I don't understand because I have done a lot of multiple-source file programs before and I never got this error where the constructor is not found. It seems like the compiler is not able to dynamically bind the template code to the instantiation of the template. Also, I have put a macro guard on the .h file but it is not shown below.
The source codes are below:
Vec.cpp
#include "Vec.h"
using namespace std;
template<class T>
Vec<T>::Vec() {
create();
}
template<class T>
Vec<T>::Vec( size_type n, const value_type& t ){
create(n,t);
}
template<class T>
Vec<T>::Vec(const Vec& v)
{
create(v.begin(), v.end());
}
template<class T>
Vec<T>::~Vec(){
uncreate();
}
template<class T>
void Vec<T>::create()
{
data = avail = limit = 0;
}
template<class T>
void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data,limit, val);
}
template<class T>
void Vec<T>::create(const_iterator i, const_iterator j) {
data = alloc.allocate(j-i);
limit = avail = uninitialized_copy(i, j, data);
}
template<class T>
void Vec<T>::uncreate() {
if (data) {
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data,limit-data);
}
data = limit = avail =0;
}
template<class T> void Vec<T>::grow() {
size_type new_size = max ( 2 * (limit-data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = unitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template<class T> void Vec<T>::unchecked_append(const T& val) {
alloc.construct(avail++, val);
}
template<class T>
void Vec<T>::push_back(const T& t){
if ( avail == limit )
grow();
unchecked_append(t);
}
Vec.h
template<class T> class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec();
Vec( size_type n, const T& t=T() );
Vec(const Vec& v);
Vec& operator=(const Vec& v);
~Vec();
void push_back(const T& t);
inline size_type size() const { return limit - data; }
inline iterator begin() {return data;}
inline const_iterator begin() const { return data; }
inline iterator end() { return limit; }
inline const_iterator end() const { return limit; }
inline T& operator[](size_type i){
return data[i];
}
const T& operator[](size_type i) const { return data[i]; }
private:
iterator data;
iterator limit;
iterator avail;
//facilities for memory allocation
allocator<T> alloc;
//allocate and initialize the underlying array
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
//destroy the elements in the array and free the memory
void uncreate();
//support functions for push_back
void grow();
void unchecked_append(const T&);
};
main.cpp
int main(void) {
Vec<double> test;
}
In order for the compiler to generate the code, it must see both the template definition and the specific types used to for the template.
So, in main.cpp add line just #include "Vec.cpp" at the top.
Compile using
g++ main.cpp -Wall -o main -I. <-- Notice Vec.cpp removed now.
This will make sure the template declaration and implementation are together during compilation, at same time implementation is still separated from declaration.
Another alternative is to include this .cpp while at the end of Vec.h as suggested in the above commented link of SO by juanchopanza
For more details Ref :- Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file ?
Related
I'm almost done with the implementation of a std::vector type of vector (hopefully) but I'm having a little bug in the code and I can't seem to find where. Basically when I build Vector and use push_back, the vector automatically allocates new memory for more elements (specifically twice the size of the vector) but when doing so, the "extra space" is initialized to 0's and I don't know why.
Here is my code:
Vector.h
#include <memory>
#include <cstddef>
template <class T>
class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec(){create();}
Vec(size_type n, const T& val = T()) {create(n, val);}
~Vec() {uncreate();}
//copy constructor
Vec(const Vec& v) {create(v.begin(), v.end());}
//assignment operator
Vec& operator=(const Vec&);
size_type size() const {return limit - data;}
//index operators
T& operator[](size_type i) {return data[i];}
const T& operator[](size_type i) const {return data[i];}
iterator begin() {return data;}
const_iterator begin() const {return data;}
iterator end() {return limit;}
const_iterator end() const {return limit;}
void push_back(const T&);
private:
iterator data; //1st element
iterator avail; //one past last constructed element
iterator limit; //one past last available element
//Memory management
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
template <class T>
void Vec<T>::push_back(const T& val){
if(avail == limit)
grow();
unchecked_append(val);
}
template <class T>
Vec<T>& Vec<T>::operator=(const Vec& rhs){
//self-assign
if(&rhs != this){
uncreate;
create(rhs.begin(), rhs.end());
}
return *this;
}
// Empty Vector, pointers to 0
template <class T> void Vec<T>::create(){
data = avail = limit = 0;
}
// Allocate memory for (size)
template <class T> void Vec<T>::create(size_type n, const T& val){
data = alloc.allocate(n); // returns pointer to first element
limit = avail = data +n;
std::uninitialized_fill(data, limit, val);
}
template <class T> void Vec<T>::create(const_iterator i, const_iterator j){
data = alloc.allocate(j-i);
limit = avail =std::uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate(){
if(data){
iterator it = avail;
while(it != data)
alloc.destroy(--it);
// Free space
alloc.deallocate(data, limit - data);
}
// Empty Vector
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow(){
// Allocate twice the space we had
size_type new_size = std::max(2 * (limit - data), ptrdiff_t(1));
// Allocate new space and copy to new space
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
// Return old space used
uncreate();
// Reset pointers to point to new space
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template <class T> void Vec<T>::unchecked_append(const T& val){
alloc.construct(avail++, val);
}
And this is how I created the vector
main.cpp
#include "vector.h"
#incude <iostream>
using namespace std;
int main(){
Vec<int> v1;
v1.push_back(12);
v1.push_back(9);
v1.push_back(74);
v1.push_back(22);
Vec<int> v2 = v1;
v2.push_back(70); //After doing this the vector is (12, 9, 74, 22, 70, 0, 0, 0)
for(auto e: v2) cout << e << " ";
cout << endl;
}
Thanks for any help and also any feedback on the actual code is appreciated. :)
The problem is (i do not know if it is really problem)
template <class T> void Vec<T>::grow(){
// Allocate twice the space we had
size_type new_size = std::max(2 * (limit - data), ptrdiff_t(1));
....
....
}
No need to do any vector copy to reproduce your problem.
The problem can be seen even in first vector after inserting 5th elemnt
v1.push_back(12);
v1.push_back(9);
v1.push_back(74);
v1.push_back(22);
v1.push_back(22); //5th elemnt.
now printing the size;
cout<<v1.size()<<endl; - it says 8.
Even the std vector, after resize(), it is normal behavior to print zeros for the empty elements.
Look at this example:
http://en.cppreference.com/w/cpp/container/vector/resize
if you do not want additional zeros, you have to tweak your ":new_size" calculation.
My function prototype looks as follows:
// Purpose: finds an element in the ArrayList
// Parameters: 'x' is value to be found in the ArrayList
// Returns: the position of the first occurrance of 'x' in the list, or -1 if 'x' is not found.
int find(const T& x) const;
It is placed in the class ArrayList
template <typename T>
class ArrayList
{
private:
int m_size; // current number of elements
int m_max; // maximum capacity of array m_data
T* m_data; // array to store the elements
T m_errobj; // dummy object to return in case of error
public:
int find(const T& x) const;
My definition is:
template <typename T>
int find(const T& x) const
{
int i;
while (m_data[i]!=x && m_data[i]!=NULL)
i++;
if (m_data[i]=x)
return i;
else
return (-1);
}
Whenever I compile, I receive the error in the title, and an error that m_data is not declared in the scope. How do I fix this?
Edit: I changed the definition to
int ArrayList<T>:: find(const T& x) const
I got a ton of errors
int ArrayList:: find(const T& x) const
didn't work either
Templates must be defined in headers. In your case, you are spliting it in .h/.cpp. In order to work, you need to define it together with your class definition. Something like this:
template <typename T>
class ArrayList
{
private:
int m_size; // current number of elements
int m_max; // maximum capacity of array m_data
T* m_data; // array to store the elements
T m_errobj; // dummy object to return in case of error
public:
int find(const T& x) const;
};
#include "x.hpp"
and define it in a file x.hpp
template <typename T>
int ArrayList<T>::find(const T& x) const
{
int i;
while (m_data[i]!=x && m_data[i]!=NULL)
i++;
if (m_data[i]=x)
return i;
else
return (-1);
}
Note that this has the same effect as you have defined everything in a unique header file
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I'm early in the stages of learning C++ and I'm working with classes. After recreating the functionality of a vector with my own class, the next step is to create my own version of a string using my new Vec class. I guess I'm too new to know how all these #includes interact but after searching many similar questions, it seems I'm just missing one somewhere. But it doesn't make sense to me because I assume I'm including the correct header where it's needed.
Str.h
#ifndef GUARD_Str_h
#define GUARD_Str_h
#include <algorithm>
#include <iterator>
#include "Vec.h"
class Str {
public:
typedef Vec<char>::size_type size_type;
Str() { }
Str(size_type n, const char& val): data(n, val) { }
Str(const char* cp) {
std::copy(cp, (cp + std::strlen(cp)), std::back_inserter(data));
}
private:
Vec<char> data;
};
#endif
Vec.h
#ifndef GUARD_Vec_h
#define GUARD_Vec_h
#include <cstring>
#include <memory>
#include <algorithm>
template <class T> class Vec {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
typedef std::ptrdiff_t difference_type;
typedef T& reference;
typedef const T& const_reference;
Vec() { create(); } //constructor
explicit Vec(size_type n, const T& val = T()) { create(n, val); } //constructor
Vec(const Vec& v) { create(v.begin(), v.end()); } //copy constructor
~Vec() { uncreate(); } //deconstructor
size_type size() const { return (avail - data); }
void push_back(const T&);
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }
Vec& operator=(const Vec&);
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return avail; }
const_iterator end() const { return avail; }
private:
iterator data; //first element in the Vec
iterator avail; //one past the last element in the Vec
iterator limit; //one past the last availible space in the Vec
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
template <class T> Vec<T>& Vec<T>::operator=(const Vec& v)
{
if (v != this) {
uncreate();
create(v.begin(), v.end());
}
return *this;
}
template <class T> void Vec<T>::push_back(const T& val)
{
if (avail == limit)
grow();
unchecked_append(val);
}
template <class T> void Vec<T>::create()
{
data = avail = limit = 0;
}
template <class T> void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = (data + n);
std::unintialized_fill(data, avail, val);
}
template <class T> void Vec<T>::create(const_iterator beg, const_iterator end)
{
data = alloc.allocate(end - beg);
limit = avail = std::uninitialized_copy(beg, end, data);
}
template <class T> void Vec<T>::uncreate()
{
if (data) {
iterator iter = avail;
while (iter != data)
alloc.destroy(--iter);
alloc.deallocate(data, (limit - data));
}
data = avail = limit = 0;
}
template <class T> void Vec<T>::grow()
{
size_type new_size = std::max((2 * (limit - data)), difference_type(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, limit, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = (data + new_size);
}
template <class T> void Vec<T>::unchecked_append(const T& val)
{
alloc.construct(avail++, val);
}
#endif
Before I even wrote the Str class, when I was just testing my Vec class, everything worked fine. I included Vec.h and could use my Vec in place of a vector.
Now with this new Str class that I made, I will include Str.h in my main source file and when I compile, it complains about std::uninitialized_copy not being a member of std. I know that it's defined in the memory header and can't figure out how to get it to see it, no matter where I put the include. I'm sure I'm just doing something wrong with the way classes interact with #includes but I'm new. If someone could help out I'd appreciate it.
std::unintialized_fill does not exist. You want std::uninitialized_fill with an i before the t.
Chapter 11 of Accelerated C++ covers implementing template classes, using a simplified version of the STL's vector class as an example. Exercise 11-6 wants us to add the .erase() and .clear() methods to the class, so first I copied the final code directly from the book and tried to compile, but it failed. I then moved all the function definitions into the .h file (removing the Vec<T>:: etc. stuff as necessary) and compiled just my main.cpp, which worked.
Here's all of my code:
main.cpp
#include <iostream>
#include "Vec.h"
using std::cout;
using std::endl;
int main()
{
Vec<int> v;
for (int i = 1; i < 10; ++i)
v.push_back(i);
for(Vec<int>::const_iterator iter = v.begin();
iter != v.end(); ++iter)
cout << *iter << endl;
return 0;
}
Vec.h
#ifndef GUARD_Vec_h
#define GUARD_Vec_h
#include <cstddef>
#include <memory>
template <class T> class Vec {
public:
// member variables
typedef T* iterator;
typedef const T* const_iterator;
typedef std::size_t size_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
// constructors + destructors
Vec() { create(); }
explicit Vec(size_type n, const T& t = T()) { create(n, t); }
Vec(const Vec& v) { create(v.begin(), v.end()); }
~Vec() { uncreate(); }
// methods
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }
void push_back(const T& t) {
if (avail == limit)
grow();
unchecked_append(t);
}
size_type size() const { return avail - data; }
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return avail; }
const_iterator end() const { return avail; }
private:
iterator data;
iterator avail;
iterator limit;
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
#endif GUARD_Vec_h
Vec.cpp
#include <algorithm>
#include <cstddef>
#include <memory>
#include "Vec.h"
using std::allocator;
using std::max;
using std::uninitialized_copy;
using std::uninitialized_fill;
using std::ptrdiff_t;
template <class T> void Vec<T>::create()
{
data = avail = limit = 0;
}
template <class T> void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data, limit, val);
}
template <class T> void Vec<T>::create(const_iterator i, const_iterator j)
{
data = alloc.allocate(j - i);
limit = avail = uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate()
{
if (data) {
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data, limit - data);
}
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow()
{
size_type new_size = max(2 * (limit - data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = uninitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template <class T> void Vec<T>::unchecked_append(const T& val)
{
alloc.construct(avail++, val);
}
Why doesn't this compile?
The problem is with any and all of the template functions. When you write a Template function, the compiler instantiates the template--that is, it creates a specific version of the function with the types you need to run the code. So when you call
Vec<int> v;
The compiler makes code for a Vec and any function of that type you call. The compiler needs to have access to the templated function definitions while making the code for the main file, because it has to know what kind of code to make for the object file before it goes to link the other files.
I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.
Here is the definition of the class template
// GenericContainer.hpp
// --------------------------------------
class ContainerItem
{
protected:
virtual ContainerItem& getInvalid() = 0;
public:
virtual ~ContainerItem();
bool isValid() const;
};
template<typename D, typename B>
class IsDerivedFrom
{
static void Constraints(D* p)
{
B* pb = p; // this line only works if 'D' inherits 'B'
pb = p; // suppress warnings about unused variables
}
protected:
void IsDerivedFrom2() { void(*p)(D*) = Constraints; }
};
// Force it to fail in the case where B is void
template<typename D>
class IsDerivedFrom<D, void>
{
void IsDerivedFrom2() { char* p = (int*)0; /* error */ }
};
template <class T>
class GenericContainer : public IsDerivedFrom<T, ContainerItem>
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
// Class to be contained (PURPOSELY, does not derive from ContainerItem)
// Data.hpp
//----------------------------------------------------------------
class Data
{ /* implem details */};
// Container for Data items
// Dataset.h
// ----------------------------------------------------------------------------
#include "GenericContainer.hpp"
#include "Data.hpp"
class Dataset: public GenericContainer<Data>
{
public:
Data& getInvalid();
};
// C++ source
// -----------------------------------------------------------
#include "Dataset.hpp"
Dataset ds;
Can anyone explain why the code above compiles?.
[Edit]
The code above should NOT compile for two reasons:
The class 'Data' does NOT derive from ContainerItem, and yet it can be stored in GenericContainer (as illustrated by the class Dataset). Incidentally, this issue has now been resolved thanks to the answer given by Omifarious and jdv
The class 'Data' does NOT implement the pure virtual method declared in the ABC ContainerItem - using the fixes recommended in the answers below, the first issue (enforcement of policy) is resolved, however the compiler fails to notice that Data does not implement the getInvalid() method of the ContainerItem 'interface'. Why is the compiler missing this glaring mistake?
BTW, compiler and OS details are:
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Change IsDerivedFrom2 to IsDerivedFrom and it fails to compile in just the expected manner.
The problem is that a method from a template class is never instantiated if it isn't called. Changing the name makes it a constructor, so it then ends up being called by the constructors of classes derived from IsDerivedFrom. It will still compile to empty code. The compiler will optimize it away the dead assignment.
I would recommend you not write template code like this yourself if you can manage to use Boost, particularly is_base_of from the Boost type traits library.
In particular, your GenericContainer template can be more simply and easily implemented this way using Boost:
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
template <class T>
class GenericContainer
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
GenericContainer() {
BOOST_STATIC_ASSERT( (::boost::is_base_of<ContainerItem, T>::value) );
}
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
The Constraints function is not generated because IsDerivedFrom2 is never referenced. This is required behavior for C++. Maybe it helps to call it from the constructor. Otherwise, check the boost library for functionality like this.