I am trying to make a sorting function for a ring buffer class object, but it gives me the below error and I couldn't remove it:
template placeholder type 'ring' must be followed by a simple declarator-id
argument list for class template "ring" is missing
'arr' was not declared in this scope
(arr) was declared in the parameter of the function but yet it does not recognize it
hopefully someone can explain what is wrong with the sorting function, here is below my code:
#include<iostream>
#include<initializer_list>
template<typename T>
void swap(T& x, T& y) {
T temp;
temp = x;
x = y;
y = temp;
}
template<typename T>
class ring {
public:
class iterator;
public:
ring(std::initializer_list<T>elements) {
sze = elements.size();
value = new T[sze];
for (auto& i : elements) {
add(i);
}
}
T& get(int x) { return value[x]; }
std::size_t size() { return sze; }
void add(T v) {
value[index++] = v;
if (index == sze)
index = 0;
}
iterator begin() { return iterator(0, *this); }
iterator end() { return iterator(sze, *this); }
friend void BubbleSort(ring& arr, std::size_t n);
~ring() { delete[] value; }
private:
int index = 0;
std::size_t sze;
T* value;
};
template<typename T>
class ring<T>::iterator {
public:
iterator(int x, ring& y) :index(x), obj(y) {}
iterator& operator++() { index++; return *this; }
iterator& operator++(int x) { index++; return *this; }
bool operator!=(const iterator& other) { return index != other.index; }
T& operator*() { return obj.get(index); }
private:
int index;
ring& obj;
};
template<typename T>
void BubbleSort(ring& arr, std::size_t n) { // here is the function of the sorting.
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr.value[j] > arr.value[j + 1]) {
swap(arr.value[j], arr.value[j + 1]);
}
}
}
}
Your friendly compiler showed you already the problem and in which line it happened.
And it gave you 3 error messages telling you exactly what the problem is.
See my compiler output
Your class ring needs a template parameter. So same as in line 49 of the picture.
The below will compile.
#include<iostream>
#include<initializer_list>
template<typename T>
void swap(T& x, T& y) {
T temp;
temp = x;
x = y;
y = temp;
}
template<typename T>
class ring {
public:
class iterator;
public:
ring(std::initializer_list<T>elements) {
sze = elements.size();
value = new T[sze];
for (auto& i : elements) {
add(i);
}
}
T& get(int x) { return value[x]; }
std::size_t size() { return sze; }
void add(T v) {
value[index++] = v;
if (index == sze)
index = 0;
}
iterator begin() { return iterator(0, *this); }
iterator end() { return iterator(sze, *this); }
friend void BubbleSort(ring& arr, std::size_t n);
~ring() { delete[] value; }
private:
int index = 0;
std::size_t sze;
T* value;
};
template<typename T>
class ring<T>::iterator {
public:
iterator(int x, ring& y) :index(x), obj(y) {}
iterator& operator++() { index++; return *this; }
iterator& operator++(int x) { index++; return *this; }
bool operator!=(const iterator& other) { return index != other.index; }
T& operator*() { return obj.get(index); }
private:
int index;
ring& obj;
};
template<typename T>
void BubbleSort(ring<T>& arr, std::size_t n) { // here is the function of the sorting.
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr.value[j] > arr.value[j + 1]) {
swap(arr.value[j], arr.value[j + 1]);
}
}
}
}
I did not check the functionality. Sorting a ringbuffer sounds somehow strange . . .
I wrote a simple B-Tree, with Node is defined as:
class Node {
Node* parent = nullptr;
std::uint32_t index = 0;
std::uint32_t height = 1;
std::vector<T> key;
std::vector<unique_alloc_ptr<Node>> child;
// ... details ...
};
where unique_alloc_ptr is the unique_ptr using my custom allocator.
template <typename T>
using unique_alloc_ptr = std::unique_ptr<T, std::function<void(T*)>>;
template <typename T>
unique_alloc_ptr<T> make_unique_fixed(FixedAllocator<T>& alloc) {
T* ptr = alloc.allocate(1);
alloc.construct(ptr);
std::function<void(T*)> deleter;
auto deleter_ = [](T* p, FixedAllocator<T>& alloc) {
alloc.destroy(p);
alloc.deallocate(p, 1);
};
deleter = [&deleter_, &alloc](auto&& PH1) { return deleter_(std::forward<decltype(PH1)>(PH1), alloc); };
return std::unique_ptr<T, decltype(deleter)>(ptr, deleter);
}
The custom allocator uses the memory pool as:
template <typename T>
class FixedAllocator {
struct Chunk {
// details ...
unsigned char data_[blockSize_ * numBlocks_];
};
// details ...
std::vector<Chunk> chunks_;
and my B-Tree uses the memory pool as member variable:
class BTree {
class Node {
// ...
};
// details ...
FixedAllocator<Node> alloc;
unique_alloc_ptr<Node> root;
};
But this gives segfault. As I guess, double free is the problem.
When lifetime of BTree ends, FixedAllocator<Node> is destroyed,
and its internal buffer std::vector<Chunk> is also destroyed.
The problem is, unique_alloc_ptr<Node> is also destroyed as well,
calling destructor of std::vector<unique_alloc_ptr<Node>> child,
which uses FixedAllocator<Node> as internal memory pool,
so double free problem occurs.
How can I solve this problem?
EDIT: Detailed implementation of FixedAllocator
template <typename T>
class FixedAllocator {
struct Chunk {
static constexpr std::size_t blockSize_ = sizeof(T);
static constexpr unsigned char numBlocks_ = FixedAllocator::numBlocks_;
Chunk() {
unsigned char i = 0;
for (unsigned char * p = &data_[0]; i != numBlocks_; p += blockSize_) {
*p = ++i;
}
}
void* allocate() {
unsigned char* result = &data_[firstAvailableBlock_ * blockSize_];
firstAvailableBlock_ = *result;
--blocksAvailable_;
return result;
}
void deallocate(void* p) {
assert(p >= &data_[0]);
auto* toRelease = static_cast<unsigned char*>(p);
assert((toRelease - &data_[0]) % blockSize_ == 0);
*toRelease = firstAvailableBlock_;
firstAvailableBlock_ = static_cast<unsigned char>((toRelease - &data_[0]) / blockSize_);
assert(firstAvailableBlock_ == (toRelease - &data_[0]) / blockSize_);
++blocksAvailable_;
}
bool hasBlock(void* p, std::size_t chunkLength) const {
auto* pc = static_cast<unsigned char*>(p);
return (&data_[0] <= pc) && (pc < &data_[chunkLength]);
}
[[nodiscard]] bool hasAvailable() const {
return (blocksAvailable_ == numBlocks_);
}
[[nodiscard]] bool isFilled() const {
return !blocksAvailable_;
}
unsigned char data_[blockSize_ * numBlocks_];
unsigned char firstAvailableBlock_ = 0;
unsigned char blocksAvailable_ = numBlocks_;
};
private:
static constexpr std::size_t blockSize_ = sizeof(T);
static constexpr unsigned char numBlocks_ = std::numeric_limits<unsigned char>::max();
std::vector<Chunk> chunks_;
Chunk* allocChunk_ = nullptr;
Chunk* deallocChunk_ = nullptr;
Chunk* emptyChunk_ = nullptr;
public:
using value_type = T;
void* doAllocate() {
if (!allocChunk_ || !allocChunk_->blocksAvailable_) {
auto it = chunks_.begin();
for (; ; ++it) {
if (it == chunks_.end()) {
chunks_.emplace_back();
allocChunk_ = &chunks_.back();
deallocChunk_ = &chunks_.front();
break;
}
if (it->blocksAvailable_) {
allocChunk_ = &*it;
break;
}
}
}
assert(allocChunk_);
assert(allocChunk_->blocksAvailable_);
return allocChunk_->allocate();
}
value_type* allocate(std::size_t n) {
assert(n == 1);
auto* p = static_cast<value_type*>(doAllocate());
return p;
}
Chunk* findVicinity(void* p) const {
assert(!chunks_.empty() && deallocChunk_);
const std::size_t chunkLength = numBlocks_ * blockSize_;
// bidirectional search
Chunk* lo = deallocChunk_;
Chunk* hi = deallocChunk_ + 1;
const Chunk* lbound = &chunks_.front();
const Chunk* hbound = &chunks_.back() + 1;
if (hi == hbound) {
hi = nullptr;
}
for (;;) {
if (lo) {
if (lo->hasBlock(p, chunkLength)) {
return lo;
}
if (lo == lbound) {
lo = nullptr;
if (!hi) {
break;
}
} else {
--lo;
}
}
if (hi) {
if (hi->hasBlock(p, chunkLength)) {
return hi;
}
if (++hi == hbound) {
hi = nullptr;
if (!lo) {
break;
}
}
}
}
return nullptr;
}
void deallocate(void* p, std::size_t n) noexcept {
assert(n == 1);
assert(!chunks_.empty());
assert(&chunks_.front() <= deallocChunk_);
assert(&chunks_.back() >= deallocChunk_);
assert(&chunks_.front() <= allocChunk_);
assert(&chunks_.back() >= allocChunk_);
Chunk* foundChunk = nullptr;
const std::size_t chunkLength = numBlocks_ * blockSize_;
if (deallocChunk_->hasBlock(p, chunkLength)) {
foundChunk = deallocChunk_;
} else {
foundChunk = findVicinity(p);
}
assert(foundChunk && foundChunk->hasBlock(p, chunkLength));
deallocChunk_ = foundChunk;
// double free check
assert(emptyChunk_ != deallocChunk_);
assert(!deallocChunk_->hasAvailable());
assert(!emptyChunk_ || emptyChunk_->hasAvailable());
deallocChunk_->deallocate(p);
if (deallocChunk_->hasAvailable()) {
// only release chunk if there are 2 empty chunks.
if (emptyChunk_) {
// if last chunk is empty, just let deallocChunk_ points
// to empty chunk, and release the last.
// otherwise, swap two and release an empty chunk
Chunk* lastChunk = &chunks_.back();
if (lastChunk == deallocChunk_) {
deallocChunk_ = emptyChunk_;
} else if (lastChunk != emptyChunk_) {
std::swap(*emptyChunk_, *lastChunk);
}
assert(lastChunk->hasAvailable());
chunks_.pop_back();
if ((allocChunk_ == lastChunk) || (allocChunk_->isFilled())) {
allocChunk_ = deallocChunk_;
}
}
emptyChunk_ = deallocChunk_;
}
}
template <typename... Args>
void construct (value_type* p, Args&&... args) {
std::construct_at(p, std::forward<Args>(args)...);
}
void destroy(value_type* p) {
std::destroy_at(p);
}
};
I am trying to implement a template Array class that can hold pointers as well as primitive data types.
But when calling the destructor, the pointers in that array are not properly deleting.
So I am trying to convert each item in that array to its corresponding class and call delete. But I met with an issue. I'm not able to convert T type to my class pointer.
My intention is to delete items in that array. Can someone please help in this?
template <class T>
class LIBRARY_EXPORT MyArrayT
{
public:
MyArrayT()
{
this->count = 0;
}
~MyArrayT()
{
if ((this->count) > 0)
{
//std::cout << "Deleting Array values" << std::endl;
delete[] this->values;
//free(this->values);
/*for (int i = 0; i < this->count; i++)
{
delete this->values[i];
}*/
this->count = 0;
}
}
void SetValue(std::vector< T > items)
{
//Delete existing memory
delete[] this->values;
this->count = items.size();
if (this->count > 0)
{
this->values = new T[this->count];
for (int i = 0; i < count; i++)
{
this->values[i] = items[i];
}
}
}
void SetValue(T items, int index)
{
if (this->count > index)
{
this->values[index] = items;
}
}
T GetValue(int index)
{
if (this->count > index)
{
return this->values[index];
}
return NULL;
}
T* GetValue()
{
return this->values;
}
_int64 GetCount()
{
return this->count;
}
private:
_int64 count;
T* values;
};
class LIBRARY_EXPORT MyString
{
public:
MyString();
~MyString();
void SetValue(std::string str);
std::string GetValue();
_int64 GetCount();
private:
_int64 count;
char* value;
};
int main()
{
MyArrayT<MyString*>* MyArrayValue = new MyArrayT<MyString*>() ;
vector<MyString*> value9;
MyString* opt1 = new MyString();
opt1->SetValue("Option: 1");
value9.push_back(opt1);
MyArrayValue->SetValue(value9);
MyArrayT<int>* MyArrayValueInt = new MyArrayT<int>() ;
vector<int> value1;
value1.push_back(1);
value1.push_back(2);
MyArrayValueInt->SetValue(value1);
delete MyArrayValue; //Calling this delete doesn't calling the ~MyString() destructor
delete MyArrayValueInt;
}
I have a class called DiGraph, that contains an array to Node pointers:
DiGraph::DiGraph(int Size) : count(0){
nodes = new Node *[Size];
}
Now I want to define the function getNodes() which is essentially supposed to return the array in list form:
Liste<Node*> DiGraph::getNodes() {
Liste<Node*> nlist(count+1);
for (int i = 0; i < count; i++) {
nlist.append(nodes[i]);
}
return nlist;
}
At the end of the function the nlist is filled correctly but somehow the resulting copy at the function call does not contain the correct pointers but all other variables.
DiGraph a(6);
a.addNode(new Node("A", 50, 50));
Liste<Node*> gN = a.getNodes(); //gN does not contain the same pointers that were returned
The class 'Liste' is basically a dynamic array with templates and some fancy functions.
Now I think the answer is probably gonna be that nlist gets destroyed after the function ends but the pointers still remain valid in the nodes variable so why does the copy contain invalid ones?
EDIT
This is the list class:
#ifndef _LISTE_H
#define _LISTE_H
// -------------------------------------------------------------------
template <typename T>
class Liste {
private:
T *_values;
int _last;
int _size;
bool isFull();
int find(T value);
void increase();
void decrease();
public:
Liste(int size = 8);
Liste(Liste &list);
~Liste();
void append(T value);
void remove(T value);
T getValueAt(int pos);
int size();
T operator[](int pos);
};
// -------------------------------------------------------------------
template <typename T>
Liste<T>::Liste(int size) {
_size = size;
_last = 0;
_values = new T[size];
}
template <typename T>
Liste<T>::Liste(Liste &list) {
_size = list._size;
_last = list._last;
_values = list._values;
}
// -------------------------------------------------------------------
template <typename T>
Liste<T>::~Liste() {
delete[] _values;
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::increase() {
T *tmp = new T[_size * 2];
for (int i = 0; i < _size; i++)
tmp[i] = _values[i];
delete[] _values;
_values = tmp;
_size *= 2;
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::decrease() {
_size /= 2;
T *tmp = new T[_size];
for (int i = 0; i < _size; i++)
tmp[i] = _values[i];
delete[] _values;
_values = tmp;
}
// -------------------------------------------------------------------
template <typename T>
bool Liste<T>::isFull() {
return _last == _size;
}
// -------------------------------------------------------------------
template <typename T>
int Liste<T>::find(T val) {
int pos;
for (pos = 0; pos < _last; pos++)
if (_values[pos] == val)
return pos;
return -1;
}
// -------------------------------------------------------------------
template <typename T>
T Liste<T>::getValueAt(int pos) {
if (pos < 0 || pos >= _last)
throw "OutOfBoundsException";
return _values[pos];
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::append(T val) {
if (isFull())
increase();
_values[_last] = val;
_last += 1;
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::remove(T val) {
int pos = find(val);
if (pos == -1)
throw "ValueNotFoundException";
for (; pos < _last - 1; pos++)
_values[pos] = _values[pos + 1];
_last -= 1;
if (_last < _size / 4)
decrease();
}
// -------------------------------------------------------------------
template <typename T>
int Liste<T>::size() {
return _last;
}
// -------------------------------------------------------------------
template <typename T>
T Liste<T>::operator[](int pos) {
return getValueAt(pos);
}
#endif
template <typename T>
Liste<T>::Liste(Liste &list) {
_size = list._size;
_last = list._last;
_values = list._values;
}
What this code does is make the new Liste object (constructed from the copy constructor) point to the memory address of an existing Liste object. But this object will be destroyed so you get a dangling pointer. You need to perform a hard copy of the values.
template <typename T>
Liste<T>::Liste(const Liste &list) { // <--- const
_size = list._size;
_last = list._last;
_values = new T[_size];
for( std::size_t iter = 0 ; iter < _size ; ++iter )
{
_values[iter] = list._values[iter];
}
}
It is good modern practice to wrap pointer members to a smart pointer (eg unique_ptr<>). This way you will never forget to delete everything and object hold is guaranteed to be cleaned up even in a case of an incomplete constructor (exception thrown).
If you plan to return-by-value you should research into how to make your Liste class move-aware
Your T operator[](int pos); could also return a contained item by reference to allow immediate modification of the object ( T& operator[](int pos); ) cause, as it is, it returns a copy of the object that lies at _values[pos] and is most likely not what you want. Similarly for your T getValueAt(int pos); public method.
Having an issue when trying to pass a const reference value within a range based for loop to a
function that takes a const reference value. Look at commments within the range based for loop to see what I'm talking about. It doesn't seem to be an issue with my implementation of 'unordered_vector' which is what I thought at first since it prints correctly outside the function. Why does this fail? It works if I pass by value, so I'm a bit confused here. Looks like it is referencing the reference which doesn't seem to be correct behaviour.
void println(const vec3& p)
{
std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}
void printlnvalue(vec3 p)
{
std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}
const int vector_length = 7;
int _tmain(int argc, _TCHAR* argv[])
{
unordered_vector<vec3> vectors(vector_length);
for (int i = 0; i < vector_length; ++i)
{
vectors.push_back(vec3(0.1f*i, 0.2f*i, 0.3f*i));
}
for (const auto& i : vectors)
{
//Prints correctly
printlnvalue(i);
//Function that does same thing but doesn't print correctly.
println(i);
}
return 0;
}
Output
<0,0,0>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.1,0.2,0.3>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.2,0.4,0.6>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.3,0.6,0.9>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.4,0.8,1.2>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.5,1,1.5>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.6,1.2,1.8>
<8.68805E-044,2.66467E-038,4.12969E-039>
unordered_vector declaration
template<typename T>
class unordered_vector
{
private:
T* m_data;
size_t m_size;
size_t m_capacity;
public:
typedef unordered_vector_iterator<T> iterator;
typedef unordered_vector_iterator<T> const const_iterator;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
size_t size(void) const;
size_t capacity(void) const;
bool empty(void) const;
void resize(size_t count);
void reserve(size_t count);
void push_back(const T& val);
const_iterator begin(void) const;
const_iterator end(void) const;
void shrink_to_fit(void);
void erase(size_t i);
void erase(const T& val);
void erase_all(const T& val);
T& operator[](size_t i);
T operator[](size_t i) const;
unordered_vector& operator=(const unordered_vector& copy);
unordered_vector& operator=(unordered_vector&& mov);
unordered_vector(void);
unordered_vector(size_t _size);
unordered_vector(unordered_vector& copy);
unordered_vector(unordered_vector&& mov);
~unordered_vector(void);
};
unordered_vector definition
template<typename T>
/*
Returns the number of active units within
the unordered_vector.
*/
size_t unordered_vector<T>::size(void) const
{
return m_size;
}
template<typename T>
/*
Returns the potential size of the vector
before it would have to resize.
*/
size_t unordered_vector<T>::capacity(void) const { return m_capacity; }
template<typename T>
/*
Returns true if no active units are within the vector. size() == 0
*/
bool unordered_vector<T>::empty(void) const
{
return m_size == 0;
}
template<typename T>
/*
This resizes the vector where anything between the parameter and
size is part of the capacity.
*/
void unordered_vector<T>::reserve(size_t count)
{
if (count > m_capacity)
{
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Will resize the vector but each of the units is
active if the buffer is increased. If the size
is reduced then items at the end are truncated.
*/
void unordered_vector<T>::resize(size_t count)
{
if (count > m_capacity)
{
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
//Make default values at each location
for (; m_size < count; ++m_size)
{
}
}
else if (count < m_capacity)
{
if (count < m_size) m_size = count;
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Shrinks capacity so capacity() == size().
*/
void unordered_vector<T>::shrink_to_fit(void)
{
if (m_size != m_capacity && m_data != nullptr)
{
m_capacity = m_size;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
if (m_data != nullptr)
free(m_data);
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Will copy a value into the next open space of the vector.
If there isn't room for an additional item it will resize
the vector to accomadate it.
*/
void unordered_vector<T>::push_back(const T& val)
{
//If we don't have any more room we need
//to resize the array
if (m_size == m_capacity)
{
m_capacity = m_capacity == 0 ? 8 : m_capacity * 2;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
if (m_data != nullptr)
{
//Copy preexisting data byte for byte
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
m_data[m_size++] = val;
}
template<typename T>
/*
Random accessor for writing to array.
*/
T& unordered_vector<T>::operator[](size_t i)
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
template<typename T>
/*
Random accessor for reading from array.
*/
T unordered_vector<T>::operator[](size_t i) const
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
template<typename T>
/*
Constant time erase. It reorders the vector
internally to allow this.
*/
void unordered_vector<T>::erase(size_t i)
{
assert(i >= 0 && i < m_size);
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
}
}
template<typename T>
/*
Will remove the first value that matches
val if it exists. In the event no value is
found the request is ignored.
*/
void unordered_vector<T>::erase(const T& val)
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i] == val)
{
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
}
break;
}
}
}
template<typename T>
/*
Will remove all values that match the parameter.
If no items match the parameter then the request
is ignored.
*/
void unordered_vector<T>::erase_all(const T& val)
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i] == val)
{
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
//Haven't checked the back yet
//so we need to recheck i
i--;
}
}
}
}
template<typename T>
/*
Initializes the vector with 0 capacity.
*/
unordered_vector<T>::unordered_vector(void)
: m_data(nullptr), m_size(0), m_capacity(0) {}
template<typename T>
/*
Initializes the vector with a capacity() equal to
the parameter.
*/
unordered_vector<T>::unordered_vector(size_t count)
: m_data(nullptr), m_size(0), m_capacity(0)
{
reserve(count);
}
template<typename T>
/*
Will copy data from another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>& vector)
{
if (vector.m_capacity > 0)
{
m_data = malloc(sizeof(T)*vector.m_capacity);
if (vector.m_size > 0)
memcpy(m_data, vector.m_data, sizeof(T)*vector.m_size);
}
m_capacity = vector.m_capacity;
m_size = vector.m_size;
}
template<typename T>
/*
Move constructor to effeciently transfer data between
a temporary and another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>&& mov)
{
m_data = mov.m_data;
m_capacity = mov.m_capacity;
m_size = mov.m_size;
mov.m_data = nullptr;
}
template<typename T>
/*
Destructor that deallocates memory
if any was allocated. Will not deallocate
memory if move semantic was invoked.
*/
unordered_vector<T>::~unordered_vector(void)
{
if (m_data != nullptr)
{
free(m_data);
m_data = nullptr;
}
m_size = 0;
m_capacity = 0;
}
template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(const unordered_vector<T>& copy)
{
if (m_data != nullptr)
{
free(m_data);
m_data = nullptr;
}
if (copy.m_capacity > 0)
{
m_data = malloc(sizeof(T)*copy.capacity());
if (copy.m_size > 0)
memcpy(m_data, copy.m_data, sizeof(T)*copy.m_size)
}
m_capacity = copy.m_capacity;
m_size = copy.m_size;
return *this;
}
template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(unordered_vector<T>&& mov)
{
if (m_data != nullptr)
free(m_data);
m_data = mov.m_data;
m_capacity = mov.m_capacity;
m_size = mov.m_size;
mov.m_data = nullptr;
}
template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::begin(void) const
{
return unordered_vector_iterator<T>(this, 0);
}
template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::end(void) const
{
return unordered_vector_iterator<T>(this, m_size);
}
iterator declaration
template<typename T>
class unordered_vector_iterator
{
private:
const unordered_vector<T>* const m_container;
size_t m_index;
public:
unordered_vector_iterator& operator++(void);
unordered_vector_iterator& operator++(int);
unordered_vector_iterator& operator--(void);
unordered_vector_iterator& operator--(int);
T& operator*(void) const;
T& operator->(void) const;
bool operator==(const unordered_vector_iterator& iter) const;
bool operator!=(const unordered_vector_iterator& iter) const;
unordered_vector_iterator(const unordered_vector<T>* const container, size_t index)
: m_container(container), m_index(index) {}
};
iterator definition
template<typename T>
bool unordered_vector_iterator<T>::operator==(const unordered_vector_iterator<T>& iter) const
{
return iter.m_index == m_index && iter.m_container == m_container;
}
template<typename T>
bool unordered_vector_iterator<T>::operator!=(const unordered_vector_iterator<T>& iter) const
{
return iter.m_index != m_index || iter.m_container != m_container;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator++(void)
{
++m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator++(int)
{
++m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator--(void)
{
--m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator--(int)
{
--m_index;
return *this;
}
template<typename T>
T& unordered_vector_iterator<T>::operator*(void) const
{
return (*m_container)[m_index];
}
template<typename T>
T& unordered_vector_iterator<T>::operator->(void) const
{
return (*m_container)[m_index];
}
vec3 declaration
struct vec3
{
public:
float x, y, z;
//vector functions
void normalize(void);
float magnitude(void) const;
float magnitudeSq(void) const;
bool operator==(vec3 o) const;
bool operator!=(vec3 o) const;
//Constructors
vec3(float _x, float _y, float _z)
: x(_x), y(_y), z(_z) {}
vec3(float s)
: x(s), y(s), z(s) {}
vec3()
: x(0.f), y(0.f), z(0.f) {}
};
You might need to declare another operator in unordered_vector to return a const T&. That's how it's declared in the STL vector class:
typedef const_reference const T&;
const_reference operator[](size_t idx) const;
The issue with the code is that the const method does not return a reference value.
Also for the code to work it has to return a non-constant reference value for some reason. Not a great answer but it works flawlessly now by changing the const index operator to this.
template<typename T>
/*
Random accessor for reading from array.
*/
T& unordered_vector<T>::operator[](size_t i) const
{
assert(i >= 0 && i < m_size);
return m_data[i];
}