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.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 10 months ago.
Improve this question
I am trying to run this program on provided google test cases but all the test cases fail.
Function String operator+(const char& str) const appends a char at the end of the String.
Function String operator+(const String& str) const appends a String at the end of the String
Function String operator+(char*& str) const appends a String at the end of the String.
Where is the problem?
#include<iostream>
#include<string>
#include <cstdlib>
using namespace std;
class String
{
private:
char* s;
int size;
public:
String()
{
s = NULL;
size = 0;
}
String(const char* str)
{
size = strlen(str);
s = new char[size];
for (int i = 0; i < size; i++)
s[i] = str[i];
}
String(const String& obj)
{
size = obj.size;
s = new char[size];
for (int i = 0; i < size; i++)
s[i] = obj[i];
}
String(int x)
{
size = x;
s = new char[size];
}
char& operator[](int i)
{
return *(s + i);
}
const char operator[](int i) const
{
return *(s + i);
}
String operator+(const char& str) const
{
String s1;
s1.s += str;
return s1;
}
operator int() const
{
int m;
m = size;
return m;
}
String operator+(const String& str) const
{
String iSt = "";
int length = 0;
length = strlen(s);
length += strlen(str.s);
iSt.s = new char[length];
strcpy(iSt.s, s);
strcat(iSt.s, str.s);
return iSt;
}
String operator+(char*& str) const
{
String iSt = "";
int length = 0;
length = strlen(s);
length += strlen(str);
iSt.s = new char[length];
strcpy(iSt.s,s);
strcat(iSt.s,str);
return iSt;
}
};
TEST(String, ArithmeticOperatorsplus) {
String s1("abcd");
String s2;
s2 = s1 + 'e';
ASSERT_EQ('d', s2[3]);
ASSERT_EQ('e', s2[4]);
char* c = (char*)"asdfgh";
s2 = s1 + c;
ASSERT_EQ('d', s2[3]);
ASSERT_EQ('a', s2[4]);
ASSERT_EQ('g', s2[8]);
ASSERT_EQ(10, (int)s2);
String s3 = s1 + s2;
ASSERT_EQ(14, (int)s3);
ASSERT_EQ('s', s3[9]);
}
The biggest problem here is that you are basically still on C and not on C++.
You are using C-functions like strlen and `strcat which even do not compile with my C++ compiler.
Then, you forget the terminating '\0' character, if you copy from C-string literals. And then, neither strlen nor strcat can work correctly.
You are calling '+=' but have no operator defined. This will not work.
You did not define assignment operators at all. The compiler generated assignments will copy the pointer and not do a deep copy.
You are doing a lot of new but never release the memory with delete.
I get 25 compiler warnings wit "-wall".
So, you need to refactor a lot.
I once create a similar dynamic array (string), which is far from perfect, but it may give you an idea, how to implement your own String . . .
#include <iostream>
#include <sstream>
#include <initializer_list>
// -----------------------------------------------------------------------------------------------
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
// The Dynamic Array has an initial capacity.
// If more elements will be added, there will be a reallocation with double capacity
static constexpr unsigned int InitialCapacity{ 8 };
// Internal data ------------------------------------------------------------------------------
T* data{}; // Dynamic Storage for Data
unsigned int numberOfElements{}; // Number of elements currently in the container
unsigned int capacity{ InitialCapacity }; // Current maximum capacity of the container
public:
// Construction and Destruction ---------------------------------------------------------------
DynamicArray(); // Default constructor. Allocate new memory
DynamicArray(const unsigned int size); // Constructor for a given size. Allocate new memory
DynamicArray(const DynamicArray& other); // Copy constructor. Make a deep copy
DynamicArray(DynamicArray&& other); // Move constructor
// Special constructors
template <class Iterator> DynamicArray(Iterator begin, Iterator end); // Initialize from range
template <int N> DynamicArray(const T(&other)[N]); // Initialize from C_Sytle array,e.g. a string literal
template <int N> DynamicArray(T(&other)[N]);
DynamicArray(const std::initializer_list<T>& list); // Take data from initializer list
~DynamicArray(); // Destructor: Release previously allocated memory
// Housekeeping ---------------------------------------------------------------
bool empty() const; // Do we have elements in the container? Do not mix up with capacity
void clear(); // Clear will not delete anything. Just set element count to 0
unsigned int size() const; // How many elements are in the container
// Main working functions
void push_back(const T& d); // Add a new element at the end
// Operators for class------------------------ ---------------------------------------------------------------
T operator[] (const unsigned int i) const; // Index operator, get data at given index. No boundary check
T& operator[] (const unsigned int i); // Index operator, get data at given index. No boundary check
DynamicArray& operator=(const DynamicArray& other); // Assignment
DynamicArray& operator=(DynamicArray&& other); // Move Assignment
// Add iterator properties to class ---------------------------------------------------------------
class iterator { // Local class for iterator
T* iter{}; // This will be the iterator
T* begin{}; // For boundary check
T* end{}; // For boundary check
public: // Define alias names necessary for the iterator functionality
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator(T* const i, T* const b, T* const e);
// Dereferencing
reference operator *() const;
pointer operator ->() const;
// Aithmetic operations
iterator& operator ++();
iterator& operator --();
iterator operator ++(int);
iterator operator --(int);
iterator operator +(const difference_type& n) const;
iterator& operator +=(const difference_type& n);
iterator operator -(const difference_type& n) const;
iterator& operator -=(const difference_type& n);
// Comparison
bool operator != (const iterator& other) const;
bool operator == (const iterator& other) const;
bool operator < (const iterator& other) const;
bool operator > (const iterator& other) const;
bool operator <= (const iterator& other) const;
bool operator >= (const iterator& other) const;
// Reference and difference
reference operator[] (const difference_type& n);
difference_type operator-(const iterator& other) const;
};
// Begin and end function to initialize an iterator
iterator begin() const;
iterator end() const;
// Working functions dealing with iterators. More may be added
iterator erase(iterator pos);
};
// Default constructor. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray() {
data = new T[capacity];
}
// Constructor for certain size. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray(const unsigned int size) : data(new T[size]), numberOfElements(0), capacity(size) {
}
// Copy constructor
template <typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& other) { // Copy constructor. Make a deep copy
capacity = numberOfElements = other.numberOfElements;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < other.numberOfElements; ++k)
data[k] = other.data[k]; // Copy data
}
// Move constructor
template <typename T>
DynamicArray<T>::DynamicArray(DynamicArray&& other) {
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
// Range constructor
template <typename T>
template <class Iterator>
DynamicArray<T>::DynamicArray(Iterator begin, Iterator end) {
data = new T[capacity];
for (Iterator i = begin; i != end; ++i)
push_back(*i);
}
// Construct from a const C-Style Array, like for example "Hello"
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(const T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from a C-Style Array
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from an initializer list
template <typename T>
DynamicArray<T>::DynamicArray(const std::initializer_list<T>& list) {
data = new T[capacity];
for (const T& t : list) push_back(t);
}
// Destructor will release the dynamic allocated memory
template <typename T>
inline DynamicArray<T>::~DynamicArray() {
delete[] data;
} // Destructor: Release previously allocated memory
// Some houskeeping functions
template <typename T>
inline bool DynamicArray<T>::empty() const {
return numberOfElements == 0;
}
template <typename T>
inline void DynamicArray<T>::clear() {
numberOfElements = 0;
}; // Clear will not delete anything. Just set element count to 0
template <typename T>
inline unsigned int DynamicArray<T>::size() const {
return numberOfElements;
} // How many elements are in the container
// Main workhorse for a dynamic array.
// Store element, and alwaysprovide enough memory
template <typename T>
void DynamicArray<T>::push_back(const T& d) { // Add a new element at the end
if (numberOfElements >= capacity) { // Check, if capacity of this dynamic array is big enough
capacity *= 2; // Obviously not, we will double the capacity
T* temp = new T[capacity]; // Allocate new and more memory
for (unsigned int k = 0; k < numberOfElements; ++k)
temp[k] = data[k]; // Copy data from old memory to new memory
delete[] data; // Release old memory
data = temp; // And assign newly allocated memory to old pointer
}
data[numberOfElements++] = d; // And finally, store the given data at the end of the container
}
// Operators for class ------------------------ ---------------------------------------------------------------
template <typename T>
inline typename T DynamicArray<T>::operator[] (const unsigned int i) const {
return data[i];
} // Index operator, get data at given index. No boundary check
template <typename T>
inline typename T& DynamicArray<T>::operator[] (const unsigned int i) {
return data[i];
} // Index operator, get data at given index. No boundary check
// Assignement operator. Make a deep copy
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(const DynamicArray& other) {
if (this != &other) { // Prevent self-assignment
delete[] data; // Release any previosly existing memory
capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
data = new T[capacity]; // Get new memory, depending on size of other
for (unsigned int k = 0; k < numberOfElements; ++k) // Copy other data
data[k] = other.data[k];
}
return *this;
}
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(DynamicArray&& other) { // Move Assignment
if (this != &other) { // Prevent self-assignment
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
return *this;
}
// Implementation of iterator functions ---------------------------------------------------------------------
// COnstruction
template <typename T>
inline DynamicArray<T>::iterator::iterator(T* const i, T* const b, T* const e) : iter(i), begin(b), end(e) {
}; // Constructor for the iterator
// Dereferencing
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator *() const {
return *iter;
}
template <typename T>
inline typename DynamicArray<T>::iterator::pointer DynamicArray<T>::iterator::operator ->() const {
return iter;
}
// Arithmetic operations
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator ++() {
if (iter < end)
++iter;
return *this;
}
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator --() {
if (iter > begin)
--iter;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator ++(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter < end)
++(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator --(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter > begin)
--(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator +(const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++tmp;
else
while (k++)
--tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator +=(const DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++* this;
else
while (k++)
--* this;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator- (const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--tmp;
else
while (k++)
++tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator -=(const typename DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--* this;
else
while (k++)
++* this;
return *this;
}
// Comparison functions
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator[] (const typename DynamicArray<T>::iterator::difference_type& n) {
return *(iter + n);
};
template <typename T>
inline bool DynamicArray<T>::iterator::operator != (const iterator& other) const {
return iter != other.iter;
}
template <typename T>
inline bool DynamicArray<T>::iterator::operator == (const iterator& other) const {
return iter == other.iter;
}
template <typename T>
inline bool DynamicArray<T>::iterator::operator < (const iterator& other) const {
return iter < other.iter;
}
template <typename T>
inline bool DynamicArray<T>::iterator::operator > (const iterator& other) const {
return iter > other.iter;
} // Comparison
template <typename T>
inline bool DynamicArray<T>::iterator::operator <= (const iterator& other) const {
return iter <= other.iter;
} // Comparison
template <typename T>
inline bool DynamicArray<T>::iterator::operator >= (const iterator& other) const {
return iter >= other.iter;
} // Comparison
// Delta
template <typename T>
inline typename DynamicArray<T>::iterator::difference_type DynamicArray<T>::iterator::operator-(const typename DynamicArray<T>::iterator& other) const {
return iter - other.iter;
}
// ------------------------------------------------------------------------
// Get iterators for dynamic array
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::begin() const {
return iterator(data, data, data + numberOfElements);
}
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::end() const {
return iterator(data + numberOfElements, data, data + numberOfElements);
}
// ------------------------------------------------------------------------
// Any other functions for dynamic array
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::erase(typename DynamicArray<T>::iterator pos) {
iterator result{ pos };
if (pos != end()) {
while (pos != end()) {
*pos = *(pos + 1);
++pos;
}
++result;
--numberOfElements;
}
return result;
}
// --------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------
// Using the dynamic array as a String, by using char as the content
using String = DynamicArray<char>;
// Some overloads for operators for easier handling
std::istream& operator >> (std::istream& is, String& s) {
s.clear();
char c{};
is >> std::ws;
while (is.peek() != EOF and is.get(c) and not isspace(c)) {
s.push_back(c);
}
if (not s.empty()) s.push_back(0);
return is;
}
std::ostream& operator << (std::ostream& os, const String& s) {
std::ostringstream oss;
for (char c : s) if (c != '\0') oss << c;
return os << oss.str();
}
bool operator < (const String& s1, const String& s2) {
unsigned int length{ (s1.size() < s2.size()) ? s1.size() : s2.size() };
for (unsigned int k{}; k < length; ++k) {
if (s1[k] == s2[k]) continue;
if (s1[k] < s2[k]) return true;
return false;
}
return false;
}
bool operator == (const String& s1, const String& s2) {
if (s1.size() != s2.size()) return false;
for (unsigned int k{}; k < s1.size(); ++k) {
if (s1[k] != s2[k]) return false;
}
return true;
}
bool operator != (const String& s1, const String& s2) { return not (s1 == s2); }
I'm trying to create my own implementation of a doubly-linked list for learning purpose. I wanted to use a template class to do this. I have a few constructors that have the definition like this:
template <typename TYPE>
class DList {
public:
DList(const size_t size, const TYPE& val = TYPE());
template <typename ITER>
DList(ITER&& begin, ITER&& end);
};
(I have more code that I have omitted).
The first constructor copies the value provided size times (so DList<int>(5, 2) would copy the number 2, 5 times)
The second constructor copies a range (between 2 Iterators) in to the list. (so DList<int>(other.begin(), other.end()) would copy one data structure in to the DList)
The question is that how do I set the preference of the two constructors? For example if I'm instantizing my class with this line:
DList<int> listTest2(6, 2);
The compiler gets confused and chooses the iterator based constructor over the size constructor and throws errors. I want to keep the template iterator constructor because I have other classes like that one (that implements other data structures) and the iterators is a class inside the class. (so DList has a class inside it called iterator, see below for more info on what I mean).
Is there anyway to set a preference over which constructor gets called. I want it to work like how I can call std::vector<int>(other.begin, other.end) and std::vector<int>(2, 3) and both work.
I have attached the header file for my template class below:
template <typename TYPE>
class DList {
public:
struct Node {
Node* prev;
TYPE data;
Node* next;
};
class iterator {
public:
iterator();
iterator(const Node* node);
iterator(const iterator& it);
iterator& operator++(); // preincrement
iterator& operator--();
iterator operator++(int); // postincrement
iterator operator--(int);
iterator& operator=(const iterator& it);
iterator& operator-(int scale);
iterator& operator+(int scale);
bool operator==(const iterator& it);
TYPE& operator*();
TYPE& operator->();
Node* getNode();
private:
Node* data;
};
DList();
DList(const DList& other);
DList(const size_t size, const TYPE& val = TYPE());
template <typename ITER>
DList(ITER&& begin, ITER&& end);
iterator begin() const;
iterator end() const;
void clear();
void push_back(const TYPE& val);
void push_front(const TYPE& val);
void pop_back();
void pop_front();
void insert(size_t idx, const TYPE& val);
void insert(const DList<TYPE>::iterator it, const TYPE& val);
void remove(const TYPE& val);
size_t size() const;
const TYPE& operator[](size_t idx) const;
TYPE& operator[](size_t idx);
private:
Node* mHead; /// #todo convert to iterators?
Node* mTail;
size_t mSize;
};
First of all: observe that with
DList<int> listTest2(6, 2);
you call the wrong constructor because you pass the wrong first argument: the size/val contructor wait for a std::size_t for the first parameter, so an unsigned value. But you send 6, that is a signed value.
The right way to call the size/val constructor is
DList<int> listTest2(6U, 2);
Second; you use && (rvalue) references; so you can use C++11; so you can use decltype.
Not a great solution but if you really want that DList<int> (6, 2) call the size/val constructor, you can use SFINAE and enable the template constructor only when ITER typename support the unary * operation (access to pointed value, supported by iterators but not by simple integers), so
template <typename I, typename = decltype(*I{})>
DList (I && begin, I && end)
The following is a full working (minimal) example
#include <iostream>
#include <vector>
template <typename T>
struct DList
{
DList (std::size_t const size, T const & val = T{})
{ std::cout << "constructor 1" << std::endl; }
template <typename I, typename = decltype(*I{})>
DList (I && begin, I && end)
{ std::cout << "constructor 2" << std::endl; }
};
int main()
{
std::vector<int> vi { 2, 3, 5, 7, 11, 13 };
DList<int>(6, 2); // print constructor 1
DList<int>(vi.cbegin(), vi.cend()); // print constructor 2
}
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 ?
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.